line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package JE; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# If you are looking at the source code (which you are obviously doing |
4
|
|
|
|
|
|
|
# if you are reading this), note that '# ~~~' is my way of marking |
5
|
|
|
|
|
|
|
# something to be done still (except in this sentence). |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# Note also that comments like "# E 7.1" refer to the indicated |
8
|
|
|
|
|
|
|
# clause (7.1 in this case) in the ECMA-262 standard. |
9
|
|
|
|
|
|
|
|
10
|
99
|
|
|
99
|
|
1518394
|
use 5.008004; |
|
99
|
|
|
|
|
286
|
|
|
99
|
|
|
|
|
3255
|
|
11
|
99
|
|
|
99
|
|
395
|
use strict; |
|
99
|
|
|
|
|
115
|
|
|
99
|
|
|
|
|
2314
|
|
12
|
99
|
|
|
99
|
|
372
|
use warnings; no warnings 'utf8'; |
|
99
|
|
|
99
|
|
191
|
|
|
99
|
|
|
|
|
2273
|
|
|
99
|
|
|
|
|
346
|
|
|
99
|
|
|
|
|
166
|
|
|
99
|
|
|
|
|
4004
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.064'; |
15
|
|
|
|
|
|
|
|
16
|
99
|
|
|
99
|
|
418
|
use Carp 'croak'; |
|
99
|
|
|
|
|
118
|
|
|
99
|
|
|
|
|
5162
|
|
17
|
99
|
|
|
99
|
|
19872
|
use JE::Code 'add_line_number'; |
|
99
|
|
|
|
|
249
|
|
|
99
|
|
|
|
|
4604
|
|
18
|
99
|
|
|
99
|
|
16078
|
use JE::_FieldHash; |
|
99
|
|
|
|
|
189
|
|
|
99
|
|
|
|
|
4676
|
|
19
|
99
|
|
|
99
|
|
475
|
use Scalar::Util 1.09 qw'blessed refaddr weaken'; |
|
99
|
|
|
|
|
1582
|
|
|
99
|
|
|
|
|
11972
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our @ISA = 'JE::Object'; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
require JE::Null ; |
24
|
|
|
|
|
|
|
require JE::Number ; |
25
|
|
|
|
|
|
|
require JE::Object ; |
26
|
|
|
|
|
|
|
require JE::Object::Function; |
27
|
|
|
|
|
|
|
require JE::Parser ; |
28
|
|
|
|
|
|
|
require JE::Scope ; |
29
|
|
|
|
|
|
|
require JE::String ; |
30
|
|
|
|
|
|
|
require JE::Undefined ; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=encoding UTF-8 |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 NAME |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
JE - Pure-Perl ECMAScript (JavaScript) Engine |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 VERSION |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Version 0.064 (alpha release) |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
The API is still subject to change. If you have the time and the interest, |
43
|
|
|
|
|
|
|
please experiment with this module (or even lend a hand :-). |
44
|
|
|
|
|
|
|
If you have any ideas for the API, or would like to help with development, |
45
|
|
|
|
|
|
|
please e-mail the author. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 SYNOPSIS |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
use JE; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
$j = new JE; # create a new global object |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
$j->eval('({"this": "that", "the": "other"}["this"])'); |
54
|
|
|
|
|
|
|
# returns "that" |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
$parsed = $j->parse('new Array(1,2,3)'); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
$rv = $parsed->execute; # returns a JE::Object::Array |
59
|
|
|
|
|
|
|
$rv->value; # returns a Perl array ref |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
$obj = $j->eval('new Object'); |
62
|
|
|
|
|
|
|
# create a new object |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
$foo = $j->{document}; # get property |
65
|
|
|
|
|
|
|
$j->{document} = $obj; # set property |
66
|
|
|
|
|
|
|
$j->{document} = {}; # gets converted to a JE::Object |
67
|
|
|
|
|
|
|
$j->{document}{location}{href}; # autovivification |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
$j->method(alert => "text"); # invoke a method |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# create global function from a Perl subroutine: |
73
|
|
|
|
|
|
|
$j->new_function(print => sub { print @_, "\n" } ); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
$j->eval(<<'--end--'); |
76
|
|
|
|
|
|
|
function correct(s) { |
77
|
|
|
|
|
|
|
s = s.replace(/[EA]/g, function(s){ |
78
|
|
|
|
|
|
|
return ['E','A'][+(s=='E')] |
79
|
|
|
|
|
|
|
}) |
80
|
|
|
|
|
|
|
return s.charAt(0) + |
81
|
|
|
|
|
|
|
s.substring(1,4).toLowerCase() + |
82
|
|
|
|
|
|
|
s.substring(4) |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
print(correct("ECMAScript")) // :-) |
85
|
|
|
|
|
|
|
--end-- |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=head1 DESCRIPTION |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
JE, short for JavaScript::Engine (imaginative, isn't it?), is a pure-Perl |
90
|
|
|
|
|
|
|
JavaScript engine. Here are some of its |
91
|
|
|
|
|
|
|
strengths: |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=over 4 |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=item - |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Easy to install (no C compiler necessary*) |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=item - |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
The parser can be extended/customised to support extra (or |
102
|
|
|
|
|
|
|
fewer) language features (not yet complete) |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item - |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
All JavaScript datatypes can be manipulated directly from Perl (they all |
107
|
|
|
|
|
|
|
have overloaded operators) |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=item - |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
The JavaScript datatypes provide C methods for compatibility with |
112
|
|
|
|
|
|
|
L. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=back |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
JE's greatest weakness is that it's slow (well, what did you expect?). It |
117
|
|
|
|
|
|
|
also uses and leaks lots of memory. (There is an experimental |
118
|
|
|
|
|
|
|
L module that solves this if you load |
119
|
|
|
|
|
|
|
it first and then call C on the JE object when |
120
|
|
|
|
|
|
|
you have finished with it.) |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
* If you are using perl 5.9.3 or lower, then L is |
123
|
|
|
|
|
|
|
required. Recent versions of it require L, an XS module |
124
|
|
|
|
|
|
|
(which requires a compiler of course), but version 0.02 of the former is |
125
|
|
|
|
|
|
|
just pure Perl with no XS dependencies. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
There is currently an experimental version of the run-time engine, which is |
128
|
|
|
|
|
|
|
supposed to be faster, although it currently makes compilation slower. (If |
129
|
|
|
|
|
|
|
you serialise the compiled code and use that, you should notice a |
130
|
|
|
|
|
|
|
speed-up.) It will eventually replace the current one when it is complete. |
131
|
|
|
|
|
|
|
(It does not yet respect tainting or max_ops, or report line numbers |
132
|
|
|
|
|
|
|
correctly.) You can activate it by setting to 1 the ridiculously named |
133
|
|
|
|
|
|
|
YES_I_WANT_JE_TO_OPTIMISE environment variable, which is just a |
134
|
|
|
|
|
|
|
temporary hack that will later be removed. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head1 USAGE |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head2 Simple Use |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
If you simply need to run a few JS functions from Perl, create a new JS |
141
|
|
|
|
|
|
|
environment like this: |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
my $je = new JE; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
If necessary, make Perl subroutines available to JavaScript: |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
$je->new_function(warn => sub { warn @_ }); |
148
|
|
|
|
|
|
|
$je->new_function(ok => \&Test::More::ok); |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Then pass the JavaScript functions to C: |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
$je->eval(<<'___'); |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
function foo() { |
155
|
|
|
|
|
|
|
return 42 |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
// etc. |
158
|
|
|
|
|
|
|
___ |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# or perhaps: |
161
|
|
|
|
|
|
|
use File::Slurp; |
162
|
|
|
|
|
|
|
$je->eval(scalar read_file 'functions.js'); |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Then you can access those function from Perl like this: |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$return_val = $je->{foo}->(); |
167
|
|
|
|
|
|
|
$return_val = $je->eval('foo()'); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
The return value will be a special object that, when converted to a string, |
170
|
|
|
|
|
|
|
boolean or number, will behave exactly as in JavaScript. You can also use |
171
|
|
|
|
|
|
|
it as a hash, to access or modify its properties. (Array objects can be |
172
|
|
|
|
|
|
|
used as arrays, too.) To call one of its |
173
|
|
|
|
|
|
|
JS methods, you should use the C method: |
174
|
|
|
|
|
|
|
C<< $return_val->method('foo') >>. See L for more information. |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head2 Custom Global Objects |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
To create a custom global object, you have to subclass JE. For instance, |
179
|
|
|
|
|
|
|
if all you need to do is add a C property that refers to the global |
180
|
|
|
|
|
|
|
object, then override the C method like this: |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
package JEx::WithSelf; |
183
|
|
|
|
|
|
|
@ISA = 'JE'; |
184
|
|
|
|
|
|
|
sub new { |
185
|
|
|
|
|
|
|
my $self = shift->SUPER::new(@_); |
186
|
|
|
|
|
|
|
$self->{self} = $self; |
187
|
|
|
|
|
|
|
return $self; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head2 Using Perl Objects from JS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
See C, below. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 Writing Custom Data Types |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
See L. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head1 METHODS |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
See also L<< C >>, which this |
201
|
|
|
|
|
|
|
class inherits from, and L<< C >>. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=over 4 |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item $j = JE->new( %opts ) |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
This class method constructs and returns a new JavaScript environment, the |
208
|
|
|
|
|
|
|
JE object itself being the global object. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
The (optional) options it can take are C and C, which |
211
|
|
|
|
|
|
|
correspond to the methods listed below. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=cut |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
our $s = qr.[\p{Zs}\s\ck]*.; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub new { |
218
|
106
|
|
|
106
|
1
|
7810
|
my $class = shift; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# I can't use the usual object and function constructors, since |
221
|
|
|
|
|
|
|
# they both rely on the existence of the global object and its |
222
|
|
|
|
|
|
|
# 'Object' and 'Function' properties. |
223
|
|
|
|
|
|
|
|
224
|
106
|
50
|
|
|
|
412
|
if(ref $class) { |
225
|
0
|
|
|
|
|
0
|
croak "JE->new is a class method and cannot be called " . |
226
|
|
|
|
|
|
|
"on a" . ('n' x ref($class) =~ /^[aoeui]/i) . ' ' . |
227
|
|
|
|
|
|
|
ref($class). " object." |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Commented lines here are just for reference: |
231
|
|
|
|
|
|
|
my $self = bless \{ |
232
|
|
|
|
|
|
|
#prototype => (Object.prototype) |
233
|
|
|
|
|
|
|
#global => ... |
234
|
|
|
|
|
|
|
keys => [], |
235
|
|
|
|
|
|
|
props => { |
236
|
|
|
|
|
|
|
Object => bless(\{ |
237
|
|
|
|
|
|
|
#prototype => (Function.prototype) |
238
|
|
|
|
|
|
|
#global => ... |
239
|
|
|
|
|
|
|
#scope => bless [global], JE::Scope |
240
|
|
|
|
|
|
|
func_name => 'Object', |
241
|
|
|
|
|
|
|
func_argnames => [], |
242
|
|
|
|
|
|
|
func_args => ['global','args'], |
243
|
|
|
|
|
|
|
function => sub { # E 15.2.1 |
244
|
15
|
|
|
15
|
|
48
|
return JE::Object->new( @_ ); |
245
|
|
|
|
|
|
|
}, |
246
|
|
|
|
|
|
|
constructor_args => ['global','args'], |
247
|
|
|
|
|
|
|
constructor => sub { |
248
|
24
|
|
|
24
|
|
69
|
return JE::Object->new( @_ ); |
249
|
|
|
|
|
|
|
}, |
250
|
|
|
|
|
|
|
keys => [], |
251
|
|
|
|
|
|
|
props => { |
252
|
|
|
|
|
|
|
#length => JE::Number->new(1), |
253
|
|
|
|
|
|
|
prototype => bless(\{ |
254
|
|
|
|
|
|
|
#global => ... |
255
|
|
|
|
|
|
|
keys => [], |
256
|
|
|
|
|
|
|
props => {}, |
257
|
|
|
|
|
|
|
}, 'JE::Object') |
258
|
|
|
|
|
|
|
}, |
259
|
|
|
|
|
|
|
prop_readonly => { |
260
|
|
|
|
|
|
|
prototype => 1, |
261
|
|
|
|
|
|
|
length => 1, |
262
|
|
|
|
|
|
|
}, |
263
|
|
|
|
|
|
|
prop_dontdel => { |
264
|
|
|
|
|
|
|
prototype => 1, |
265
|
|
|
|
|
|
|
length => 1, |
266
|
|
|
|
|
|
|
}, |
267
|
|
|
|
|
|
|
}, 'JE::Object::Function'), |
268
|
|
|
|
|
|
|
Function => bless(\{ |
269
|
|
|
|
|
|
|
#prototype => (Function.prototype) |
270
|
|
|
|
|
|
|
#global => ... |
271
|
|
|
|
|
|
|
#scope => bless [global], JE::Scope |
272
|
|
|
|
|
|
|
func_name => 'Function', |
273
|
|
|
|
|
|
|
func_argnames => [], |
274
|
|
|
|
|
|
|
func_args => ['scope','args'], |
275
|
|
|
|
|
|
|
function => sub { # E 15.3.1 |
276
|
20
|
|
|
|
|
93
|
JE::Object::Function->new( |
277
|
20
|
|
|
20
|
|
22
|
$${$_[0][0]}{global}, |
278
|
|
|
|
|
|
|
@_[1..$#_] |
279
|
|
|
|
|
|
|
); |
280
|
|
|
|
|
|
|
}, |
281
|
|
|
|
|
|
|
constructor_args => ['scope','args'], |
282
|
|
|
|
|
|
|
constructor => sub { |
283
|
34
|
|
|
|
|
172
|
JE::Object::Function->new( |
284
|
34
|
|
|
34
|
|
44
|
$${$_[0][0]}{global}, |
285
|
|
|
|
|
|
|
@_[1..$#_] |
286
|
|
|
|
|
|
|
); |
287
|
|
|
|
|
|
|
}, |
288
|
106
|
|
|
|
|
4470
|
keys => [], |
289
|
|
|
|
|
|
|
props => { |
290
|
|
|
|
|
|
|
#length => JE::Number->new(1), |
291
|
|
|
|
|
|
|
prototype => bless(\{ |
292
|
|
|
|
|
|
|
#prototype=>(Object.proto) |
293
|
|
|
|
|
|
|
#global => ... |
294
|
|
|
|
|
|
|
func_argnames => [], |
295
|
|
|
|
|
|
|
func_args => [], |
296
|
|
|
|
|
|
|
function => '', |
297
|
|
|
|
|
|
|
keys => [], |
298
|
|
|
|
|
|
|
props => {}, |
299
|
|
|
|
|
|
|
}, 'JE::Object::Function') |
300
|
|
|
|
|
|
|
}, |
301
|
|
|
|
|
|
|
prop_readonly => { |
302
|
|
|
|
|
|
|
prototype => 1, |
303
|
|
|
|
|
|
|
length => 1, |
304
|
|
|
|
|
|
|
}, |
305
|
|
|
|
|
|
|
prop_dontdel => { |
306
|
|
|
|
|
|
|
prototype => 1, |
307
|
|
|
|
|
|
|
length => 1, |
308
|
|
|
|
|
|
|
}, |
309
|
|
|
|
|
|
|
}, 'JE::Object::Function'), |
310
|
|
|
|
|
|
|
}, |
311
|
|
|
|
|
|
|
}, $class; |
312
|
|
|
|
|
|
|
|
313
|
106
|
|
|
|
|
983
|
my $obj_proto = |
314
|
|
|
|
|
|
|
(my $obj_constr = $self->prop('Object')) ->prop('prototype'); |
315
|
106
|
|
|
|
|
415
|
my $func_proto = |
316
|
|
|
|
|
|
|
(my $func_constr = $self->prop('Function'))->prop('prototype'); |
317
|
|
|
|
|
|
|
|
318
|
106
|
|
|
|
|
641
|
$self->prototype( $obj_proto ); |
319
|
106
|
|
|
|
|
255
|
$$$self{global} = $self; |
320
|
|
|
|
|
|
|
|
321
|
106
|
|
|
|
|
468
|
$obj_constr->prototype( $func_proto ); |
322
|
106
|
|
|
|
|
224
|
$$$obj_constr{global} = $self; |
323
|
106
|
|
|
|
|
477
|
my $scope = $$$obj_constr{scope} = bless [$self], 'JE::Scope'; |
324
|
|
|
|
|
|
|
|
325
|
106
|
|
|
|
|
298
|
$func_constr->prototype( $func_proto ); |
326
|
106
|
|
|
|
|
192
|
$$$func_constr{global} = $self; |
327
|
106
|
|
|
|
|
214
|
$$$func_constr{scope} = $scope; |
328
|
|
|
|
|
|
|
|
329
|
106
|
|
|
|
|
2459
|
$$$obj_proto{global} = $self; |
330
|
|
|
|
|
|
|
|
331
|
106
|
|
|
|
|
306
|
$func_proto->prototype( $obj_proto ); |
332
|
106
|
|
|
|
|
201
|
$$$func_proto{global} = $self; |
333
|
|
|
|
|
|
|
|
334
|
106
|
|
|
|
|
798
|
$obj_constr ->prop( |
335
|
|
|
|
|
|
|
{name=>'length',dontenum=>1,value=>new JE::Number $self,1} |
336
|
|
|
|
|
|
|
); |
337
|
106
|
|
|
|
|
495
|
$func_constr->prop( |
338
|
|
|
|
|
|
|
{name=>'length',dontenum=>1,value=>new JE::Number $self,1} |
339
|
|
|
|
|
|
|
); |
340
|
106
|
|
|
|
|
526
|
$func_proto->prop({name=>'length', value=>0, dontenum=>1}); |
341
|
|
|
|
|
|
|
|
342
|
106
|
50
|
|
|
|
378
|
if($JE::Destroyer) { |
343
|
0
|
|
|
|
|
0
|
JE::Destroyer'register($_) for $obj_constr, $func_constr; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# Before we add anything else, we need to make sure that our global |
347
|
|
|
|
|
|
|
# true/false/undefined/null values are available. |
348
|
106
|
|
|
|
|
780
|
@{$$self}{qw{ t f u n }} = ( |
|
106
|
|
|
|
|
532
|
|
349
|
|
|
|
|
|
|
JE::Boolean->new($self, 1), |
350
|
|
|
|
|
|
|
JE::Boolean->new($self, 0), |
351
|
|
|
|
|
|
|
JE::Undefined->new($self), |
352
|
|
|
|
|
|
|
JE::Null->new($self), |
353
|
|
|
|
|
|
|
); |
354
|
|
|
|
|
|
|
|
355
|
106
|
|
|
|
|
443
|
$self->prototype_for('Object', $obj_proto); |
356
|
106
|
|
|
|
|
268
|
$self->prototype_for('Function', $func_proto); |
357
|
106
|
|
|
|
|
427
|
JE::Object::_init_proto($obj_proto); |
358
|
106
|
|
|
|
|
615
|
JE::Object::Function::_init_proto($func_proto); |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# The rest of the constructors |
362
|
|
|
|
|
|
|
# E 15.1.4 |
363
|
106
|
|
|
|
|
824
|
$self->prop({ |
364
|
|
|
|
|
|
|
name => 'Array', |
365
|
|
|
|
|
|
|
autoload => |
366
|
|
|
|
|
|
|
'require JE::Object::Array; |
367
|
|
|
|
|
|
|
JE::Object::Array::_new_constructor($global)', |
368
|
|
|
|
|
|
|
dontenum => 1, |
369
|
|
|
|
|
|
|
}); |
370
|
106
|
|
|
|
|
591
|
$self->prop({ |
371
|
|
|
|
|
|
|
name => 'String', |
372
|
|
|
|
|
|
|
autoload => |
373
|
|
|
|
|
|
|
'require JE::Object::String; |
374
|
|
|
|
|
|
|
JE::Object::String::_new_constructor($global)', |
375
|
|
|
|
|
|
|
dontenum => 1, |
376
|
|
|
|
|
|
|
}); |
377
|
106
|
|
|
|
|
534
|
$self->prop({ |
378
|
|
|
|
|
|
|
name => 'Boolean', |
379
|
|
|
|
|
|
|
autoload => |
380
|
|
|
|
|
|
|
'require JE::Object::Boolean; |
381
|
|
|
|
|
|
|
JE::Object::Boolean::_new_constructor($global)', |
382
|
|
|
|
|
|
|
dontenum => 1, |
383
|
|
|
|
|
|
|
}); |
384
|
106
|
|
|
|
|
527
|
$self->prop({ |
385
|
|
|
|
|
|
|
name => 'Number', |
386
|
|
|
|
|
|
|
autoload => |
387
|
|
|
|
|
|
|
'require JE::Object::Number; |
388
|
|
|
|
|
|
|
JE::Object::Number::_new_constructor($global)', |
389
|
|
|
|
|
|
|
dontenum => 1, |
390
|
|
|
|
|
|
|
}); |
391
|
106
|
|
|
|
|
502
|
$self->prop({ |
392
|
|
|
|
|
|
|
name => 'Date', |
393
|
|
|
|
|
|
|
autoload => |
394
|
|
|
|
|
|
|
'require JE::Object::Date; |
395
|
|
|
|
|
|
|
JE::Object::Date::_new_constructor($global)', |
396
|
|
|
|
|
|
|
dontenum => 1, |
397
|
|
|
|
|
|
|
}); |
398
|
106
|
|
|
|
|
501
|
$self->prop({ |
399
|
|
|
|
|
|
|
name => 'RegExp', |
400
|
|
|
|
|
|
|
autoload => |
401
|
|
|
|
|
|
|
'require JE::Object::RegExp; |
402
|
|
|
|
|
|
|
JE::Object::RegExp->new_constructor($global)', |
403
|
|
|
|
|
|
|
dontenum => 1, |
404
|
|
|
|
|
|
|
}); |
405
|
106
|
|
|
|
|
498
|
$self->prop({ |
406
|
|
|
|
|
|
|
name => 'Error', |
407
|
|
|
|
|
|
|
autoload => |
408
|
|
|
|
|
|
|
'require JE::Object::Error; |
409
|
|
|
|
|
|
|
JE::Object::Error::_new_constructor($global)', |
410
|
|
|
|
|
|
|
dontenum => 1, |
411
|
|
|
|
|
|
|
}); |
412
|
|
|
|
|
|
|
# No EvalError |
413
|
106
|
|
|
|
|
533
|
$self->prop({ |
414
|
|
|
|
|
|
|
name => 'RangeError', |
415
|
|
|
|
|
|
|
autoload => 'require JE::Object::Error::RangeError; |
416
|
|
|
|
|
|
|
JE::Object::Error::RangeError |
417
|
|
|
|
|
|
|
->_new_subclass_constructor($global)', |
418
|
|
|
|
|
|
|
dontenum => 1, |
419
|
|
|
|
|
|
|
}); |
420
|
106
|
|
|
|
|
529
|
$self->prop({ |
421
|
|
|
|
|
|
|
name => 'ReferenceError', |
422
|
|
|
|
|
|
|
autoload => 'require JE::Object::Error::ReferenceError; |
423
|
|
|
|
|
|
|
JE::Object::Error::ReferenceError |
424
|
|
|
|
|
|
|
->_new_subclass_constructor($global)', |
425
|
|
|
|
|
|
|
dontenum => 1, |
426
|
|
|
|
|
|
|
}); |
427
|
106
|
|
|
|
|
555
|
$self->prop({ |
428
|
|
|
|
|
|
|
name => 'SyntaxError', |
429
|
|
|
|
|
|
|
autoload => 'require JE::Object::Error::SyntaxError; |
430
|
|
|
|
|
|
|
JE::Object::Error::SyntaxError |
431
|
|
|
|
|
|
|
->_new_subclass_constructor($global)', |
432
|
|
|
|
|
|
|
dontenum => 1, |
433
|
|
|
|
|
|
|
}); |
434
|
106
|
|
|
|
|
491
|
$self->prop({ |
435
|
|
|
|
|
|
|
name => 'TypeError', |
436
|
|
|
|
|
|
|
autoload => 'require JE::Object::Error::TypeError; |
437
|
|
|
|
|
|
|
JE::Object::Error::TypeError |
438
|
|
|
|
|
|
|
->_new_subclass_constructor($global)', |
439
|
|
|
|
|
|
|
dontenum => 1, |
440
|
|
|
|
|
|
|
}); |
441
|
106
|
|
|
|
|
483
|
$self->prop({ |
442
|
|
|
|
|
|
|
name => 'URIError', |
443
|
|
|
|
|
|
|
autoload => 'require JE::Object::Error::URIError; |
444
|
|
|
|
|
|
|
JE::Object::Error::URIError |
445
|
|
|
|
|
|
|
->_new_subclass_constructor($global)', |
446
|
|
|
|
|
|
|
dontenum => 1, |
447
|
|
|
|
|
|
|
}); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# E 15.1.1 |
450
|
106
|
|
|
|
|
465
|
$self->prop({ |
451
|
|
|
|
|
|
|
name => 'NaN', |
452
|
|
|
|
|
|
|
value => JE::Number->new($self, 'NaN'), |
453
|
|
|
|
|
|
|
dontenum => 1, |
454
|
|
|
|
|
|
|
dontdel => 1, |
455
|
|
|
|
|
|
|
}); |
456
|
106
|
|
|
|
|
541
|
$self->prop({ |
457
|
|
|
|
|
|
|
name => 'Infinity', |
458
|
|
|
|
|
|
|
value => JE::Number->new($self, 'Infinity'), |
459
|
|
|
|
|
|
|
dontenum => 1, |
460
|
|
|
|
|
|
|
dontdel => 1, |
461
|
|
|
|
|
|
|
}); |
462
|
106
|
|
|
|
|
478
|
$self->prop({ |
463
|
|
|
|
|
|
|
name => 'undefined', |
464
|
|
|
|
|
|
|
value => $self->undefined, |
465
|
|
|
|
|
|
|
dontenum => 1, |
466
|
|
|
|
|
|
|
dontdel => 1, |
467
|
|
|
|
|
|
|
}); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
# E 15.1.2 |
471
|
|
|
|
|
|
|
$self->prop({ |
472
|
|
|
|
|
|
|
name => 'eval', |
473
|
|
|
|
|
|
|
value => JE::Object::Function->new({ |
474
|
|
|
|
|
|
|
scope => $self, |
475
|
|
|
|
|
|
|
name => 'eval', |
476
|
|
|
|
|
|
|
argnames => ['x'], |
477
|
|
|
|
|
|
|
function_args => [qw< args >], |
478
|
|
|
|
|
|
|
function => sub { |
479
|
104
|
|
|
104
|
|
140
|
my($code) = @_; |
480
|
104
|
100
|
|
|
|
220
|
return $self->undefined unless defined |
481
|
|
|
|
|
|
|
$code; |
482
|
103
|
100
|
|
|
|
320
|
return $code if typeof $code ne 'string'; |
483
|
101
|
|
|
|
|
138
|
my $old_at = $@; # hope it's not tied |
484
|
101
|
100
|
66
|
|
|
432
|
defined (my $tree = |
485
|
|
|
|
|
|
|
($JE::Code::parser||$self) |
486
|
|
|
|
|
|
|
->parse($code)) |
487
|
|
|
|
|
|
|
or die; |
488
|
94
|
|
|
|
|
345
|
my $ret = execute $tree |
489
|
|
|
|
|
|
|
$JE::Code::this, |
490
|
|
|
|
|
|
|
$JE::Code::scope, 1; |
491
|
|
|
|
|
|
|
|
492
|
94
|
100
|
|
|
|
354
|
ref $@ ne '' and die; |
493
|
|
|
|
|
|
|
|
494
|
88
|
|
|
|
|
119
|
$@ = $old_at; |
495
|
88
|
|
|
|
|
672
|
$ret; |
496
|
|
|
|
|
|
|
}, |
497
|
106
|
|
|
|
|
1363
|
no_proto => 1, |
498
|
|
|
|
|
|
|
}), |
499
|
|
|
|
|
|
|
dontenum => 1, |
500
|
|
|
|
|
|
|
}); |
501
|
|
|
|
|
|
|
$self->prop({ |
502
|
|
|
|
|
|
|
name => 'parseInt', |
503
|
|
|
|
|
|
|
value => JE::Object::Function->new({ |
504
|
|
|
|
|
|
|
scope => $self, |
505
|
|
|
|
|
|
|
name => 'parseInt', # E 15.1.2.2 |
506
|
|
|
|
|
|
|
argnames => [qw/string radix/], |
507
|
|
|
|
|
|
|
no_proto => 1, |
508
|
|
|
|
|
|
|
function_args => [qw< scope args >], |
509
|
|
|
|
|
|
|
function => sub { |
510
|
2687
|
|
|
2687
|
|
3376
|
my($scope,$str,$radix) = @_; |
511
|
2687
|
100
|
|
|
|
8374
|
$radix = defined $radix |
512
|
|
|
|
|
|
|
? $radix->to_number->value |
513
|
|
|
|
|
|
|
: 0; |
514
|
2687
|
100
|
100
|
|
|
14921
|
$radix == $radix and $radix != $radix+1 |
515
|
|
|
|
|
|
|
or $radix = 0; |
516
|
|
|
|
|
|
|
|
517
|
2687
|
100
|
|
|
|
4810
|
if(defined $str) { |
518
|
2686
|
|
|
|
|
6053
|
($str = $str->to_string) |
519
|
|
|
|
|
|
|
=~ s/^$s//; |
520
|
1
|
|
|
|
|
2
|
} else { $str = 'undefined' }; |
521
|
2687
|
100
|
|
|
|
13396
|
my $sign = $str =~ s/^([+-])// |
522
|
|
|
|
|
|
|
? (-1,1)[$1 eq '+'] |
523
|
|
|
|
|
|
|
: 1; |
524
|
2687
|
|
|
|
|
5108
|
$radix = (int $radix) % 2 ** 32; |
525
|
2687
|
100
|
|
|
|
6433
|
$radix -= 2**32 if $radix >= 2**31; |
526
|
2687
|
100
|
66
|
|
|
6799
|
$radix ||= $str =~ /^0x/i |
527
|
|
|
|
|
|
|
? 16 |
528
|
|
|
|
|
|
|
: 10 |
529
|
|
|
|
|
|
|
; |
530
|
2687
|
100
|
|
|
|
10928
|
$radix == 16 and |
531
|
|
|
|
|
|
|
$str =~ s/^0x//i; |
532
|
|
|
|
|
|
|
|
533
|
2687
|
100
|
100
|
|
|
11349
|
$radix < 2 || $radix > 36 and return |
534
|
|
|
|
|
|
|
JE::Number->new($self,'nan'); |
535
|
|
|
|
|
|
|
|
536
|
2043
|
|
|
|
|
10897
|
my @digits = (0..9, 'a'..'z')[0 |
537
|
|
|
|
|
|
|
..$radix-1]; |
538
|
2043
|
|
|
|
|
7647
|
my $digits = join '', @digits; |
539
|
2043
|
|
|
|
|
51962
|
$str =~ /^([$digits]*)/i; |
540
|
2043
|
|
|
|
|
6002
|
$str = $1; |
541
|
|
|
|
|
|
|
|
542
|
2043
|
|
|
|
|
2078
|
my $ret; |
543
|
2043
|
100
|
|
|
|
8867
|
if(!length $str){ |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
544
|
707
|
|
|
|
|
1116
|
$ret= 'nan' ; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
elsif($radix == 10) { |
547
|
204
|
|
|
|
|
529
|
$ret= $sign * $str; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
elsif($radix == 16) { |
550
|
161
|
|
|
|
|
556
|
$ret= $sign * hex $str; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
elsif($radix == 8) { |
553
|
28
|
|
|
|
|
127
|
$ret= $sign * oct $str; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
elsif($radix == 2) { |
556
|
28
|
|
|
|
|
1453
|
$ret= $sign * eval |
557
|
|
|
|
|
|
|
"0b$str"; |
558
|
|
|
|
|
|
|
} |
559
|
915
|
|
|
|
|
1116
|
else { my($num, $place); |
560
|
915
|
|
|
|
|
2559
|
for (reverse split //, $str){ |
561
|
1657
|
100
|
|
|
|
6965
|
$num += ($_ =~ /[0-9]/ ? $_ |
562
|
|
|
|
|
|
|
: ord(uc) - 55) |
563
|
|
|
|
|
|
|
* $radix**$place++ |
564
|
|
|
|
|
|
|
} |
565
|
915
|
|
|
|
|
1376
|
$ret= $num*$sign; |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
2043
|
|
|
|
|
6671
|
return JE::Number->new($self,$ret); |
569
|
|
|
|
|
|
|
}, |
570
|
106
|
|
|
|
|
1721
|
}), |
571
|
|
|
|
|
|
|
dontenum => 1, |
572
|
|
|
|
|
|
|
}); |
573
|
|
|
|
|
|
|
$self->prop({ |
574
|
|
|
|
|
|
|
name => 'parseFloat', |
575
|
|
|
|
|
|
|
value => JE::Object::Function->new({ |
576
|
|
|
|
|
|
|
scope => $self, |
577
|
|
|
|
|
|
|
name => 'parseFloat', # E 15.1.2.3 |
578
|
|
|
|
|
|
|
argnames => [qw/string/], |
579
|
|
|
|
|
|
|
no_proto => 1, |
580
|
|
|
|
|
|
|
function_args => [qw< scope args >], |
581
|
|
|
|
|
|
|
function => sub { |
582
|
694
|
|
|
694
|
|
1191
|
my($scope,$str,$radix) = @_; |
583
|
|
|
|
|
|
|
|
584
|
694
|
100
|
|
|
|
1460
|
defined $str or $str = ''; |
585
|
694
|
100
|
|
|
|
1652
|
ref $str eq 'JE::Number' and return $str; |
586
|
692
|
50
|
|
|
|
1323
|
ref $str eq 'JE::Object::Number' |
587
|
|
|
|
|
|
|
and return $str->to_number; |
588
|
692
|
100
|
|
|
|
3419
|
return JE::Number->new($self, $str =~ |
589
|
|
|
|
|
|
|
/^$s |
590
|
|
|
|
|
|
|
( |
591
|
|
|
|
|
|
|
[+-]? |
592
|
|
|
|
|
|
|
(?: |
593
|
|
|
|
|
|
|
(?=[0-9]|\.[0-9]) [0-9]* |
594
|
|
|
|
|
|
|
(?:\.[0-9]*)? |
595
|
|
|
|
|
|
|
(?:[Ee][+-]?[0-9]+)? |
596
|
|
|
|
|
|
|
| |
597
|
|
|
|
|
|
|
Infinity |
598
|
|
|
|
|
|
|
) |
599
|
|
|
|
|
|
|
) |
600
|
|
|
|
|
|
|
/ox |
601
|
|
|
|
|
|
|
? $1 : 'nan'); |
602
|
|
|
|
|
|
|
}, |
603
|
106
|
|
|
|
|
1371
|
}), |
604
|
|
|
|
|
|
|
dontenum => 1, |
605
|
|
|
|
|
|
|
}); |
606
|
|
|
|
|
|
|
$self->prop({ |
607
|
|
|
|
|
|
|
name => 'isNaN', |
608
|
|
|
|
|
|
|
value => JE::Object::Function->new({ |
609
|
|
|
|
|
|
|
scope => $self, |
610
|
|
|
|
|
|
|
name => 'isNaN', |
611
|
|
|
|
|
|
|
argnames => [qw/number/], |
612
|
|
|
|
|
|
|
no_proto => 1, |
613
|
|
|
|
|
|
|
function_args => ['args'], |
614
|
|
|
|
|
|
|
function => sub { |
615
|
99
|
|
100
|
99
|
|
416
|
JE::Boolean->new($self, |
616
|
|
|
|
|
|
|
!defined $_[0] || |
617
|
|
|
|
|
|
|
shift->to_number->id eq 'num:nan'); |
618
|
|
|
|
|
|
|
}, |
619
|
106
|
|
|
|
|
1207
|
}), |
620
|
|
|
|
|
|
|
dontenum => 1, |
621
|
|
|
|
|
|
|
}); |
622
|
|
|
|
|
|
|
$self->prop({ |
623
|
|
|
|
|
|
|
name => 'isFinite', |
624
|
|
|
|
|
|
|
value => JE::Object::Function->new({ |
625
|
|
|
|
|
|
|
scope => $self, |
626
|
|
|
|
|
|
|
name => 'isFinite', |
627
|
|
|
|
|
|
|
argnames => [qw/number/], |
628
|
|
|
|
|
|
|
no_proto => 1, |
629
|
|
|
|
|
|
|
function_args => ['args'], |
630
|
|
|
|
|
|
|
function => sub { |
631
|
12
|
|
|
12
|
|
14
|
my $val = shift; |
632
|
12
|
|
100
|
|
|
67
|
JE::Boolean->new($self, |
633
|
|
|
|
|
|
|
defined $val && |
634
|
|
|
|
|
|
|
($val = $val->to_number->value) |
635
|
|
|
|
|
|
|
== $val && |
636
|
|
|
|
|
|
|
$val + 1 != $val |
637
|
|
|
|
|
|
|
); |
638
|
|
|
|
|
|
|
}, |
639
|
106
|
|
|
|
|
1273
|
}), |
640
|
|
|
|
|
|
|
dontenum => 1, |
641
|
|
|
|
|
|
|
}); |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
# E 15.1.3 |
644
|
106
|
|
|
|
|
641
|
$self->prop({ |
645
|
|
|
|
|
|
|
name => 'decodeURI', |
646
|
|
|
|
|
|
|
autoload => q{ require 'JE/escape.pl'; |
647
|
|
|
|
|
|
|
JE::Object::Function->new({ |
648
|
|
|
|
|
|
|
scope => $global, |
649
|
|
|
|
|
|
|
name => 'decodeURI', |
650
|
|
|
|
|
|
|
argnames => [qw/encodedURI/], |
651
|
|
|
|
|
|
|
no_proto => 1, |
652
|
|
|
|
|
|
|
function_args => ['scope','args'], |
653
|
|
|
|
|
|
|
function => \&JE'_decodeURI, |
654
|
|
|
|
|
|
|
}) |
655
|
|
|
|
|
|
|
}, |
656
|
|
|
|
|
|
|
dontenum => 1, |
657
|
|
|
|
|
|
|
}); |
658
|
106
|
|
|
|
|
527
|
$self->prop({ |
659
|
|
|
|
|
|
|
name => 'decodeURIComponent', |
660
|
|
|
|
|
|
|
autoload => q{ require 'JE/escape.pl'; |
661
|
|
|
|
|
|
|
JE::Object::Function->new({ |
662
|
|
|
|
|
|
|
scope => $global, |
663
|
|
|
|
|
|
|
name => 'decodeURIComponent', |
664
|
|
|
|
|
|
|
argnames => [qw/encodedURIComponent/], |
665
|
|
|
|
|
|
|
no_proto => 1, |
666
|
|
|
|
|
|
|
function_args => ['scope','args'], |
667
|
|
|
|
|
|
|
function => \&JE'_decodeURIComponent |
668
|
|
|
|
|
|
|
}) |
669
|
|
|
|
|
|
|
}, |
670
|
|
|
|
|
|
|
dontenum => 1, |
671
|
|
|
|
|
|
|
}); |
672
|
106
|
|
|
|
|
538
|
$self->prop({ |
673
|
|
|
|
|
|
|
name => 'encodeURI', |
674
|
|
|
|
|
|
|
autoload => q{ require 'JE/escape.pl'; |
675
|
|
|
|
|
|
|
JE::Object::Function->new({ |
676
|
|
|
|
|
|
|
scope => $global, |
677
|
|
|
|
|
|
|
name => 'encodeURI', |
678
|
|
|
|
|
|
|
argnames => [qw/uri/], |
679
|
|
|
|
|
|
|
no_proto => 1, |
680
|
|
|
|
|
|
|
function_args => ['scope','args'], |
681
|
|
|
|
|
|
|
function => \&JE'_encodeURI, |
682
|
|
|
|
|
|
|
}) |
683
|
|
|
|
|
|
|
}, |
684
|
|
|
|
|
|
|
dontenum => 1, |
685
|
|
|
|
|
|
|
}); |
686
|
106
|
|
|
|
|
507
|
$self->prop({ |
687
|
|
|
|
|
|
|
name => 'encodeURIComponent', |
688
|
|
|
|
|
|
|
autoload => q{ require 'JE/escape.pl'; |
689
|
|
|
|
|
|
|
JE::Object::Function->new({ |
690
|
|
|
|
|
|
|
scope => $global, |
691
|
|
|
|
|
|
|
name => 'encodeURIComponent', |
692
|
|
|
|
|
|
|
argnames => [qw/uriComponent/], |
693
|
|
|
|
|
|
|
no_proto => 1, |
694
|
|
|
|
|
|
|
function_args => ['scope','args'], |
695
|
|
|
|
|
|
|
function => \&JE'_encodeURIComponent, |
696
|
|
|
|
|
|
|
}) |
697
|
|
|
|
|
|
|
}, |
698
|
|
|
|
|
|
|
dontenum => 1, |
699
|
|
|
|
|
|
|
}); |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
# E 15.1.5 / 15.8 |
702
|
106
|
|
|
|
|
2408
|
$self->prop({ |
703
|
|
|
|
|
|
|
name => 'Math', |
704
|
|
|
|
|
|
|
autoload => 'require JE::Object::Math; |
705
|
|
|
|
|
|
|
JE::Object::Math->new($global)', |
706
|
|
|
|
|
|
|
dontenum => 1, |
707
|
|
|
|
|
|
|
}); |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
# E B.2 |
710
|
106
|
|
|
|
|
568
|
$self->prop({ |
711
|
|
|
|
|
|
|
name => 'escape', |
712
|
|
|
|
|
|
|
autoload => q{ |
713
|
|
|
|
|
|
|
require 'JE/escape.pl'; |
714
|
|
|
|
|
|
|
JE::Object::Function->new({ |
715
|
|
|
|
|
|
|
scope => $global, |
716
|
|
|
|
|
|
|
name => 'escape', |
717
|
|
|
|
|
|
|
argnames => [qw/string/], |
718
|
|
|
|
|
|
|
no_proto => 1, |
719
|
|
|
|
|
|
|
function_args => ['scope','args'], |
720
|
|
|
|
|
|
|
function => \&JE'_escape, |
721
|
|
|
|
|
|
|
}) |
722
|
|
|
|
|
|
|
}, |
723
|
|
|
|
|
|
|
dontenum => 1, |
724
|
|
|
|
|
|
|
}); |
725
|
106
|
|
|
|
|
545
|
$self->prop({ |
726
|
|
|
|
|
|
|
name => 'unescape', |
727
|
|
|
|
|
|
|
autoload => q{ |
728
|
|
|
|
|
|
|
require 'JE/escape.pl'; |
729
|
|
|
|
|
|
|
JE::Object::Function->new({ |
730
|
|
|
|
|
|
|
scope => $global, |
731
|
|
|
|
|
|
|
name => 'unescape', |
732
|
|
|
|
|
|
|
argnames => [qw/string/], |
733
|
|
|
|
|
|
|
no_proto => 1, |
734
|
|
|
|
|
|
|
function_args => ['scope','args'], |
735
|
|
|
|
|
|
|
function => \&JE'_unescape, |
736
|
|
|
|
|
|
|
}) |
737
|
|
|
|
|
|
|
}, |
738
|
|
|
|
|
|
|
dontenum => 1, |
739
|
|
|
|
|
|
|
}); |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
# Constructor args |
743
|
106
|
|
|
|
|
251
|
my %args = @_; |
744
|
106
|
|
|
|
|
363
|
$$$self{max_ops} = delete $args{max_ops}; |
745
|
106
|
|
|
|
|
286
|
$$$self{html_mode} = delete $args{html_mode}; |
746
|
|
|
|
|
|
|
|
747
|
106
|
|
|
|
|
500
|
$self; |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
=item $j->parse( $code, $filename, $first_line_no ) |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
C parses the code contained in C<$code> and returns a parse |
756
|
|
|
|
|
|
|
tree (a JE::Code object). |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
If the syntax is not valid, C will be returned and C<$@> will |
759
|
|
|
|
|
|
|
contain an |
760
|
|
|
|
|
|
|
error message. Otherwise C<$@> will be a null string. |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
The JE::Code class provides the method |
763
|
|
|
|
|
|
|
C for executing the |
764
|
|
|
|
|
|
|
pre-compiled syntax tree. |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
C<$filename> and C<$first_line_no>, which are both optional, will be stored |
767
|
|
|
|
|
|
|
inside the JE::Code object and used for JS error messages. (See also |
768
|
|
|
|
|
|
|
L in the JE::Code man page.) |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=item $j->compile( STRING ) |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Just an alias for C. |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=cut |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
sub parse { |
777
|
340
|
|
|
340
|
1
|
31141
|
goto &JE::Code::parse; |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
*compile = \&parse; |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=item $j->eval( $code, $filename, $lineno ) |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
C evaluates the JavaScript code contained in C<$code>. E.g.: |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
$j->eval('[1,2,3]') # returns a JE::Object::Array which can be used as |
787
|
|
|
|
|
|
|
# an array ref |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
If C<$filename> and C<$lineno> are specified, they will be used in error |
790
|
|
|
|
|
|
|
messages. C<$lineno> is the number of the first line; it defaults to 1. |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
If an error occurs, C will be returned and C<$@> will contain the |
793
|
|
|
|
|
|
|
error message. If no error occurs, C<$@> will be a null string. |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
This is actually just |
796
|
|
|
|
|
|
|
a wrapper around C and the C method of the |
797
|
|
|
|
|
|
|
JE::Code class. |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
If the JavaScript code evaluates to an lvalue, a JE::LValue object will be |
800
|
|
|
|
|
|
|
returned. You can use this like any other return value (e.g., as an array |
801
|
|
|
|
|
|
|
ref if it points to a JS array). In addition, you can use the C and |
802
|
|
|
|
|
|
|
C methods to set/get the value of the property to which the lvalue |
803
|
|
|
|
|
|
|
refers. (See also L.) E.g., this will create a new object |
804
|
|
|
|
|
|
|
named C: |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
$j->eval('this.document')->set({}); |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
Note that I used C rather than just C, since the |
809
|
|
|
|
|
|
|
latter would throw an error if the variable did not exist. |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
=cut |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
sub eval { |
814
|
118
|
|
|
118
|
1
|
613
|
my $code = shift->parse(@_); |
815
|
118
|
100
|
|
|
|
304
|
$@ and return; |
816
|
|
|
|
|
|
|
|
817
|
115
|
|
|
|
|
449
|
$code->execute; |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=item $j->new_function($name, sub { ... }) |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=item $j->new_function(sub { ... }) |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
This creates and returns a new function object. If $name is given, |
828
|
|
|
|
|
|
|
it will become a property of the global object. |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
Use this to make a Perl subroutine accessible from JavaScript. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
For more ways to create functions, see L. |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
This is actually a method of JE::Object, so you can use it on any object: |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
$j->{Math}->new_function(double => sub { 2 * shift }); |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
=item $j->new_method($name, sub { ... }) |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
This is just like C, except that, when the function is |
842
|
|
|
|
|
|
|
called, the subroutine's first argument (number 0) will be the object |
843
|
|
|
|
|
|
|
with which the function is called. E.g.: |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
$j->eval('String.prototype')->new_method( |
846
|
|
|
|
|
|
|
reverse => sub { scalar reverse shift } |
847
|
|
|
|
|
|
|
); |
848
|
|
|
|
|
|
|
# ... then later ... |
849
|
|
|
|
|
|
|
$j->eval(q[ 'a string'.reverse() ]); # returns 'gnirts a' |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
=item $j->max_ops |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
=item $j->max_ops( $new_value ) |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
Use this to set the maximum number of operations that C (or |
857
|
|
|
|
|
|
|
JE::Code's C) will run before terminating. (You can use this for |
858
|
|
|
|
|
|
|
runaway scripts.) The exact method of counting operations |
859
|
|
|
|
|
|
|
is consistent from one run to another, but is not guaranteed to be consistent between versions of JE. In the current implementation, an |
860
|
|
|
|
|
|
|
operation means an expression or sub-expression, so a simple C |
861
|
|
|
|
|
|
|
statement with no arguments is not counted. |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
With no arguments, this method returns the current value. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
As shorthand, you can pass C<< max_ops => $foo >> to the constructor. |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
If the number of operations is exceeded, then C will return undef and |
868
|
|
|
|
|
|
|
set C<$@> to a 'max_ops (xxx) exceeded. |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=cut |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
sub max_ops { |
873
|
2207
|
|
|
2207
|
1
|
2497
|
my $self = shift; |
874
|
2207
|
100
|
|
|
|
3756
|
if(@_) { $$$self{max_ops} = shift; return } |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
3
|
|
875
|
2206
|
|
|
|
|
8911
|
else { return $$$self{max_ops} } |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
=item $j->html_mode |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
=item $j->html_mode( $new_value ) |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
Use this to turn on 'HTML mode', in which HTML comment delimiters are |
884
|
|
|
|
|
|
|
treated much like C/>. C is a boolean. Since this violates |
885
|
|
|
|
|
|
|
ECMAScript, it is off by default. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
With no arguments, this method returns the current value. |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
As shorthand, you can pass C<< html_mode => 1 >> to the constructor. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=cut |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
sub html_mode { |
894
|
391
|
|
|
391
|
1
|
560
|
my $self = shift; |
895
|
391
|
100
|
|
|
|
926
|
if(@_) { $$$self{html_mode} = shift; return } |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
7
|
|
896
|
388
|
|
|
|
|
1901
|
else { return $$$self{html_mode} } |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=item $j->upgrade( @values ) |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
This method upgrades the value or values given to it. See |
903
|
|
|
|
|
|
|
L for more detail. |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
If you pass it more |
907
|
|
|
|
|
|
|
than one |
908
|
|
|
|
|
|
|
argument in scalar context, it returns the number of arguments--but that |
909
|
|
|
|
|
|
|
is subject to change, so don't do that. |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=cut |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
fieldhash my %wrappees; |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
sub upgrade { |
916
|
29661
|
|
|
29661
|
1
|
8027611
|
my @__; |
917
|
29661
|
|
|
|
|
32299
|
my $self = shift; |
918
|
29661
|
|
|
|
|
25786
|
my($classes,$proxy_cache); |
919
|
29661
|
|
|
|
|
51882
|
for (@_) { |
920
|
24619
|
100
|
|
|
|
63441
|
if (defined blessed $_) { |
921
|
9675
|
100
|
|
|
|
31183
|
$classes or ($classes,$proxy_cache) = |
922
|
|
|
|
|
|
|
@$$self{'classes','proxy_cache'}; |
923
|
9675
|
|
|
|
|
16084
|
my $ident = refaddr $_; |
924
|
9675
|
|
|
|
|
12028
|
my $class = ref; |
925
|
|
|
|
|
|
|
push @__, exists $$classes{$class} |
926
|
|
|
|
|
|
|
? exists $$proxy_cache{$ident} |
927
|
|
|
|
|
|
|
? $$proxy_cache{$ident} |
928
|
|
|
|
|
|
|
: ($$proxy_cache{$ident} = |
929
|
|
|
|
|
|
|
exists $$classes{$class}{wrapper} |
930
|
9675
|
100
|
|
|
|
31584
|
? do { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
931
|
|
|
|
|
|
|
weaken( $wrappees{ |
932
|
1
|
|
|
|
|
5
|
my $proxy |
933
|
|
|
|
|
|
|
= $$classes{$class}{wrapper}( |
934
|
|
|
|
|
|
|
$self,$_ |
935
|
|
|
|
|
|
|
) |
936
|
|
|
|
|
|
|
} = $_); |
937
|
1
|
|
|
|
|
15
|
$proxy |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
: JE::Object::Proxy->new($self,$_) |
940
|
|
|
|
|
|
|
) |
941
|
|
|
|
|
|
|
: $_; |
942
|
|
|
|
|
|
|
} else { |
943
|
14944
|
100
|
66
|
|
|
139230
|
push @__, |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
944
|
|
|
|
|
|
|
!defined() |
945
|
|
|
|
|
|
|
? $self->undefined |
946
|
|
|
|
|
|
|
: ref($_) eq 'ARRAY' |
947
|
|
|
|
|
|
|
? JE::Object::Array->new($self, $_) |
948
|
|
|
|
|
|
|
: ref($_) eq 'HASH' |
949
|
|
|
|
|
|
|
? JE::Object->new($self, { value => $_ }) |
950
|
|
|
|
|
|
|
: ref($_) eq 'CODE' |
951
|
|
|
|
|
|
|
? JE::Object::Function->new($self, $_) |
952
|
|
|
|
|
|
|
: $_ eq '0' || $_ eq '-0' |
953
|
|
|
|
|
|
|
? JE::Number->new($self, 0) |
954
|
|
|
|
|
|
|
: JE::String->new($self, $_) |
955
|
|
|
|
|
|
|
; |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
} |
958
|
29661
|
100
|
|
|
|
216330
|
@__ > 1 ? @__ : @__ == 1 ? $__[0] : (); |
|
|
100
|
|
|
|
|
|
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
sub _upgr_def { |
962
|
|
|
|
|
|
|
# ~~~ maybe I should make this a public method named upgrade_defined |
963
|
0
|
0
|
|
0
|
|
0
|
return defined $_[1] ? shift->upgrade(shift) : undef |
964
|
|
|
|
|
|
|
} |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=item $j->undefined |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
Returns the JavaScript undefined value. |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=cut |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
sub undefined { |
974
|
1473
|
|
|
1473
|
1
|
3079
|
$${+shift}{u} |
|
1473
|
|
|
|
|
5247
|
|
975
|
|
|
|
|
|
|
} |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
=item $j->null |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
Returns the JavaScript null value. |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
=cut |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
sub null { |
987
|
4221
|
|
|
4221
|
1
|
3741
|
$${+shift}{n} |
|
4221
|
|
|
|
|
18118
|
|
988
|
|
|
|
|
|
|
} |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
=item $j->true |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
Returns the JavaScript true value. |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
=item $j->false |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
Returns the JavaScript false value. |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
=cut |
1001
|
|
|
|
|
|
|
|
1002
|
731
|
|
|
731
|
1
|
743
|
sub true { $${+shift}{t} } |
|
731
|
|
|
|
|
4841
|
|
1003
|
629
|
|
|
629
|
1
|
642
|
sub false { $${+shift}{f} } |
|
629
|
|
|
|
|
4693
|
|
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
=item $j->bind_class( LIST ) |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
(This method can create a potential security hole. Please see L, |
1011
|
|
|
|
|
|
|
below.) |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
=back |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
=head2 Synopsis |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
$j->bind_class( |
1018
|
|
|
|
|
|
|
package => 'Net::FTP', |
1019
|
|
|
|
|
|
|
name => 'FTP', # if different from package |
1020
|
|
|
|
|
|
|
constructor => 'new', # or sub { Net::FTP->new(@_) } |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
methods => [ 'login','get','put' ], |
1023
|
|
|
|
|
|
|
# OR: |
1024
|
|
|
|
|
|
|
methods => { |
1025
|
|
|
|
|
|
|
log_me_in => 'login', # or sub { shift->login(@_) } |
1026
|
|
|
|
|
|
|
chicken_out => 'quit', |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
static_methods => { |
1029
|
|
|
|
|
|
|
# etc. etc. etc. |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
to_primitive => \&to_primitive # or a method name |
1032
|
|
|
|
|
|
|
to_number => \&to_number |
1033
|
|
|
|
|
|
|
to_string => \&to_string |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
props => [ 'status' ], |
1036
|
|
|
|
|
|
|
# OR: |
1037
|
|
|
|
|
|
|
props => { |
1038
|
|
|
|
|
|
|
status => { |
1039
|
|
|
|
|
|
|
fetch => sub { 'this var never changes' } |
1040
|
|
|
|
|
|
|
store => sub { system 'say -vHysterical hah hah' } |
1041
|
|
|
|
|
|
|
}, |
1042
|
|
|
|
|
|
|
# OR: |
1043
|
|
|
|
|
|
|
status => \&fetch_store # or method name |
1044
|
|
|
|
|
|
|
}, |
1045
|
|
|
|
|
|
|
static_props => { ... } |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
hash => 1, # Perl obj can be used as a hash |
1048
|
|
|
|
|
|
|
array => 1, # or as an array |
1049
|
|
|
|
|
|
|
# OR (not yet implemented): |
1050
|
|
|
|
|
|
|
hash => 'namedItem', # method name or code ref |
1051
|
|
|
|
|
|
|
array => 'item', # likewise |
1052
|
|
|
|
|
|
|
# OR (not yet implemented): |
1053
|
|
|
|
|
|
|
hash => { |
1054
|
|
|
|
|
|
|
fetch => 'namedItem', |
1055
|
|
|
|
|
|
|
store => sub { shift->{+shift} = shift }, |
1056
|
|
|
|
|
|
|
}, |
1057
|
|
|
|
|
|
|
array => { |
1058
|
|
|
|
|
|
|
fetch => 'item', |
1059
|
|
|
|
|
|
|
store => sub { shift->[shift] = shift }, |
1060
|
|
|
|
|
|
|
}, |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
isa => 'Object', |
1063
|
|
|
|
|
|
|
# OR: |
1064
|
|
|
|
|
|
|
isa => $j->{Object}{prototype}, |
1065
|
|
|
|
|
|
|
); |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
# OR: |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
$j->bind_class( |
1070
|
|
|
|
|
|
|
package => 'Net::FTP', |
1071
|
|
|
|
|
|
|
wrapper => sub { new JE_Proxy_for_Net_FTP @_ } |
1072
|
|
|
|
|
|
|
); |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=head2 Description |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
(Some of this is random order, and probably needs to be rearranged.) |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
This method binds a Perl class to JavaScript. LIST is a hash-style list of |
1080
|
|
|
|
|
|
|
key/value pairs. The keys, listed below, are all optional except for |
1081
|
|
|
|
|
|
|
C or |
1082
|
|
|
|
|
|
|
C--you must specify at least one of the two. |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
Whenever it says you can pass a method name to a particular option, and |
1085
|
|
|
|
|
|
|
that method is expected to return a value (i.e., this does not apply to |
1086
|
|
|
|
|
|
|
C<< props => { property_name => { store => 'method' } } >>), you may append |
1087
|
|
|
|
|
|
|
a colon and a data type (such as ':String') to the method name, to indicate |
1088
|
|
|
|
|
|
|
to what JavaScript type to convert the return value. Actually, this is the |
1089
|
|
|
|
|
|
|
name of a JS function to which the return value will be passed, so 'String' |
1090
|
|
|
|
|
|
|
has to be capitalised. This also means than you can use 'method:eval' to |
1091
|
|
|
|
|
|
|
evaluate the return value of 'method' as JavaScript code. One exception to |
1092
|
|
|
|
|
|
|
this is that the special string ':null' indicates that Perl's C |
1093
|
|
|
|
|
|
|
should become JS's C, but other values will be converted the default |
1094
|
|
|
|
|
|
|
way. This is useful, for instance, if a method should return an object or |
1095
|
|
|
|
|
|
|
C, from JavaScript's point of view. This ':' feature does not stop |
1096
|
|
|
|
|
|
|
you from using double colons in method names, so you can write |
1097
|
|
|
|
|
|
|
C<'Package::method:null'> if you like, and rest assured that it will split |
1098
|
|
|
|
|
|
|
on the last colon. Furthermore, just C<'Package::method'> will also work. |
1099
|
|
|
|
|
|
|
It won't split it at all. |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=over 4 |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
=item package |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
The name of the Perl class. If this is omitted, C will be used |
1106
|
|
|
|
|
|
|
instead. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=item name |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
The name the class will have in JavaScript. This is used by |
1111
|
|
|
|
|
|
|
C and as the name of the constructor. If |
1112
|
|
|
|
|
|
|
omitted, C will be used. |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
=item constructor => 'method_name' |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=item constructor => sub { ... } |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
If C is given a string, the constructor will treat it as the |
1119
|
|
|
|
|
|
|
name of a class method of C. |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
If it is a coderef, it will be used as the constructor. |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
If this is omitted, the constructor will raise an error when called. If |
1124
|
|
|
|
|
|
|
there is already a constructor with the same name, however, it will be left |
1125
|
|
|
|
|
|
|
as it is (though methods will still be added to its prototype object). This |
1126
|
|
|
|
|
|
|
allows two Perl classes to be bound to a single JavaScript class: |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
$j->bind_class( name => 'Foo', package => 'Class::One', methods => ... ); |
1129
|
|
|
|
|
|
|
$j->bind_class( name => 'Foo', package => 'Class::Two' ); |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
=item methods => [ ... ] |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
=item methods => { ... } |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
If an array ref is supplied, the named methods will be bound to JavaScript |
1136
|
|
|
|
|
|
|
functions of the same names. |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
If a hash ref is used, the keys will be the |
1139
|
|
|
|
|
|
|
names of the methods from JavaScript's point of view. The values can be |
1140
|
|
|
|
|
|
|
either the names of the Perl methods, or code references. |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
=item static_methods |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
Like C but they will become methods of the constructor itself, not |
1145
|
|
|
|
|
|
|
of its C property. |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
=item to_primitive => sub { ... } |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
=item to_primitive => 'method_name' |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
When the object is converted to a primitive value in JavaScript, this |
1152
|
|
|
|
|
|
|
coderef or method will be called. The first argument passed will, of |
1153
|
|
|
|
|
|
|
course, be the object. The second argument will be the hint ('number' or |
1154
|
|
|
|
|
|
|
'string') or will be omitted. |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
If to_primitive is omitted, the usual valueOf and |
1157
|
|
|
|
|
|
|
toString methods will be tried as with built-in JS |
1158
|
|
|
|
|
|
|
objects, if the object does not have overloaded string/boolean/number |
1159
|
|
|
|
|
|
|
conversions. If the object has even one of those three, then conversion to |
1160
|
|
|
|
|
|
|
a primitive will be the same as in Perl. |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
If C<< to_primitive => undef >> is specified, primitivisation |
1163
|
|
|
|
|
|
|
without a hint (which happens with C<< < >> and C<==>) will throw a |
1164
|
|
|
|
|
|
|
TypeError. |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
=item to_number |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
If this is omitted, C will be |
1169
|
|
|
|
|
|
|
used. |
1170
|
|
|
|
|
|
|
If set to undef, a TypeError will be thrown whenever the |
1171
|
|
|
|
|
|
|
object is numified. |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
=item to_string |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
If this is omitted, C will be |
1176
|
|
|
|
|
|
|
used. |
1177
|
|
|
|
|
|
|
If set to undef, a TypeError will be thrown whenever the |
1178
|
|
|
|
|
|
|
object is strung. |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
=item props => [ ... ] |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
=item props => { ... } |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
Use this to add properties that will trigger the provided methods or |
1185
|
|
|
|
|
|
|
subroutines when accessed. These property definitions can also be inherited |
1186
|
|
|
|
|
|
|
by subclasses, as long as, when the subclass is registered with |
1187
|
|
|
|
|
|
|
C, the superclass is specified as a string (via C, below). |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
If this is an array ref, its elements will be the names of the properties. |
1190
|
|
|
|
|
|
|
When a property is retrieved, a method of the same name is called. When a |
1191
|
|
|
|
|
|
|
property is set, the same method is called, with the new value as the |
1192
|
|
|
|
|
|
|
argument. |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
If a hash ref is given, for each element, if the value is a simple scalar, |
1195
|
|
|
|
|
|
|
the property named by the key will trigger the method named by the value. |
1196
|
|
|
|
|
|
|
If the value is a coderef, it will be called with the object as its |
1197
|
|
|
|
|
|
|
argument when the variable is read, and with the object and |
1198
|
|
|
|
|
|
|
the new |
1199
|
|
|
|
|
|
|
value as its two arguments when the variable is set. |
1200
|
|
|
|
|
|
|
If the value is a hash ref, the C and C keys will be |
1201
|
|
|
|
|
|
|
expected to be either coderefs or method names. If only C is given, |
1202
|
|
|
|
|
|
|
the property will be read-only. If only C is given, the property |
1203
|
|
|
|
|
|
|
will |
1204
|
|
|
|
|
|
|
be write-only and will appear undefined when accessed. (If neither is |
1205
|
|
|
|
|
|
|
given, |
1206
|
|
|
|
|
|
|
it will be a read-only undefined property--really useful.) |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
=item static_props |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
Like C but they will become properties of the constructor itself, |
1211
|
|
|
|
|
|
|
not |
1212
|
|
|
|
|
|
|
of its C property. |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
=item hash |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
If this option is present, then this indicates that the Perl object |
1217
|
|
|
|
|
|
|
can be used |
1218
|
|
|
|
|
|
|
as a hash. An attempt to access a property not defined by C or |
1219
|
|
|
|
|
|
|
C will result in the retrieval of a hash element instead (unless |
1220
|
|
|
|
|
|
|
the property name is a number and C is specified as well). |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
=begin comment |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
There are several values this option can take: |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
=over 4 |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
=item * |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
One of the strings '1-way' and '2-way' (also 1 and 2 for short). This will |
1231
|
|
|
|
|
|
|
indicate that the object being wrapped can itself be used as a hash. |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
=end comment |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
The value you give this option should be one of the strings '1-way' and |
1236
|
|
|
|
|
|
|
'2-way' (also 1 and 2 for short). |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
If |
1239
|
|
|
|
|
|
|
you specify '1-way', only properties corresponding to existing hash |
1240
|
|
|
|
|
|
|
elements will be linked to those elements; |
1241
|
|
|
|
|
|
|
properties added to the object from JavaScript will |
1242
|
|
|
|
|
|
|
be JavaScript's own, and will not affect the wrapped object. (Consider how |
1243
|
|
|
|
|
|
|
node lists and collections work in web browsers.) |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
If you specify '2-way', an attempt to create a property in JavaScript will |
1246
|
|
|
|
|
|
|
be reflected in the underlying object. |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
=begin comment |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
=item * |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
A method name (that does not begin with a number). This method will be |
1253
|
|
|
|
|
|
|
called on the object with the object as the first arg (C<$_[0]>), the |
1254
|
|
|
|
|
|
|
property name as the second, and, if an assignment is being made, the new |
1255
|
|
|
|
|
|
|
value as the third. This will be a one-way hash. |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
=item * |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
A reference to a subroutine. This sub will be called with the same |
1260
|
|
|
|
|
|
|
arguments as a method. Again, this will be a one-way hash. |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=item * |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
A hash with C and C keys, which should be set to method names |
1265
|
|
|
|
|
|
|
or coderefs. Actually, you may omit C to create a one-way binding, |
1266
|
|
|
|
|
|
|
as per '1-way', above, except that the properties that correspond to hash |
1267
|
|
|
|
|
|
|
keys will be read-only as well. |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=back |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
=end comment |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
B Make this accept '1-way:String', etc. |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
=item array |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
This is just like C, but for arrays. This will also create a property |
1278
|
|
|
|
|
|
|
named 'length'. |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
=for comment |
1281
|
|
|
|
|
|
|
if passed '1-way' or '2-way'. |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
B Make this accept '1-way:String', etc. |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
=begin comment |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
=item keys |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
This should be a method name or coderef that takes the object as its first |
1290
|
|
|
|
|
|
|
argument and |
1291
|
|
|
|
|
|
|
returns a list of hash keys. This only applies if C is specified |
1292
|
|
|
|
|
|
|
and passed a method name, coderef, or hash. |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
=end comment |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
=item unwrap => 1 |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
If you specify this and it's true, objects passed as arguments to the |
1299
|
|
|
|
|
|
|
methods or code |
1300
|
|
|
|
|
|
|
refs specified above are 'unwrapped' if they are proxies for Perl objects |
1301
|
|
|
|
|
|
|
(see below). And null and undefined are converted to C. |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
This is experimental right now. I might actually make this the default. |
1304
|
|
|
|
|
|
|
Maybe this should provide more options for fine-tuning, or maybe what is |
1305
|
|
|
|
|
|
|
currently the default behaviour should be removed. If |
1306
|
|
|
|
|
|
|
anyone has any opinions on this, please e-mail the author. |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
=item isa => 'ClassName' |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
=item isa => $prototype_object |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
(Maybe this should be renamed 'super'.) |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
The name of the superclass. 'Object' is the default. To make this new |
1315
|
|
|
|
|
|
|
class's prototype object have no prototype, specify |
1316
|
|
|
|
|
|
|
C. Instead of specifying the name of the superclass, you |
1317
|
|
|
|
|
|
|
can |
1318
|
|
|
|
|
|
|
provide the superclass's prototype object. |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
If you specify a name, a constructor function by that name must already |
1321
|
|
|
|
|
|
|
exist, or an exception will be thrown. (I supposed I could make JE smart |
1322
|
|
|
|
|
|
|
enough to defer retrieving the prototype object until the superclass is |
1323
|
|
|
|
|
|
|
registered. Well, maybe later.) |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
=item wrapper => sub { ... } |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
If C is specified, all other arguments will be ignored except for |
1328
|
|
|
|
|
|
|
C (or C if C is not present). |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
When an object of the Perl class in question is 'upgraded,' this subroutine |
1331
|
|
|
|
|
|
|
will be called with the global object as its first argument and the object |
1332
|
|
|
|
|
|
|
to be 'wrapped' as the second. The subroutine is expected to return |
1333
|
|
|
|
|
|
|
an object compatible with the interface described in L. |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
If C is supplied, no constructor will be created. |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
=back |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
After a class has been bound, objects of the Perl class will, when passed |
1340
|
|
|
|
|
|
|
to JavaScript (or the C method), appear as instances of the |
1341
|
|
|
|
|
|
|
corresponding JS class. Actually, they are 'wrapped up' in a proxy object |
1342
|
|
|
|
|
|
|
(a JE::Object::Proxy |
1343
|
|
|
|
|
|
|
object), that provides the interface that JS operators require (see |
1344
|
|
|
|
|
|
|
L). If the |
1345
|
|
|
|
|
|
|
object is passed back to Perl, it is the I |
1346
|
|
|
|
|
|
|
not the original object that is returned. The proxy's C method will |
1347
|
|
|
|
|
|
|
return the original object. I if the C option above is used |
1348
|
|
|
|
|
|
|
when a class is bound, the original Perl object will be passed to any |
1349
|
|
|
|
|
|
|
methods or properties belonging to that class. B
|
1350
|
|
|
|
|
|
|
subject to change.> See L, above. |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
Note that, if you pass a Perl object to JavaScript before binding its |
1353
|
|
|
|
|
|
|
class, |
1354
|
|
|
|
|
|
|
JavaScript's reference to it (if any) will remain as it is, and will not be |
1355
|
|
|
|
|
|
|
wrapped up inside a proxy object. |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
To use Perl's overloading within JavaScript, well...er, you don't have to |
1358
|
|
|
|
|
|
|
do |
1359
|
|
|
|
|
|
|
anything. If the object has C<"">, C<0+> or C overloading, that will |
1360
|
|
|
|
|
|
|
automatically be detected and used. |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
=cut |
1363
|
|
|
|
|
|
|
|
1364
|
68
|
100
|
|
68
|
|
337
|
sub _split_meth { $_[0] =~ /(.*[^:]):([^:].*)/s ? ($1, $2) : $_[0] } |
1365
|
|
|
|
|
|
|
# This function splits a method specification of the form 'method:Func' |
1366
|
|
|
|
|
|
|
# into its two constituent parts, returning ($_[0],undef) if it is a simple |
1367
|
|
|
|
|
|
|
# method name. The [^:] parts of the regexp are to allow things like |
1368
|
|
|
|
|
|
|
# "HTML::Element::new:null" and to prevent "Foo::bar" from being turned |
1369
|
|
|
|
|
|
|
# into qw(Foo: bar). |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
sub _cast { |
1372
|
31
|
|
|
31
|
|
130
|
my ($self,$val,$type) = @_; |
1373
|
31
|
100
|
|
|
|
62
|
return $self->upgrade($val) unless defined $type; |
1374
|
29
|
100
|
|
|
|
46
|
if($type eq 'null') { |
1375
|
19
|
100
|
|
|
|
49
|
defined $val ? $self->upgrade($val) : $self->null |
1376
|
|
|
|
|
|
|
} |
1377
|
|
|
|
|
|
|
else { |
1378
|
10
|
|
|
|
|
26
|
$self->prop($type)->call($self->upgrade($val)); |
1379
|
|
|
|
|
|
|
} |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
sub _unwrap { |
1383
|
0
|
|
|
0
|
|
0
|
my ($self) = shift; |
1384
|
0
|
|
|
|
|
0
|
my @ret; |
1385
|
0
|
|
|
|
|
0
|
for(@_){ |
1386
|
0
|
0
|
|
|
|
0
|
push @ret, |
|
|
0
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
ref =~ # Check the most common classes for efficiency. |
1388
|
|
|
|
|
|
|
/^JE::(?:Object::Proxy(?:::Array)?|Undefined|Null)\z/ |
1389
|
|
|
|
|
|
|
? $_->value |
1390
|
|
|
|
|
|
|
: exists $wrappees{$_} |
1391
|
|
|
|
|
|
|
? $wrappees{$_} |
1392
|
|
|
|
|
|
|
: $_ |
1393
|
|
|
|
|
|
|
} |
1394
|
0
|
|
|
|
|
0
|
@ret; |
1395
|
|
|
|
|
|
|
} |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
sub bind_class { |
1398
|
36
|
|
|
36
|
1
|
3044
|
require JE::Object::Proxy; |
1399
|
|
|
|
|
|
|
|
1400
|
36
|
|
|
|
|
48
|
my $self = shift; |
1401
|
36
|
|
|
|
|
111
|
my %opts = @_; |
1402
|
|
|
|
|
|
|
#{ no warnings; |
1403
|
|
|
|
|
|
|
#warn refaddr $self, " ", $opts{name} , ' ' ,$opts{package}; } |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
# &upgrade relies on this, because it |
1407
|
|
|
|
|
|
|
# takes the value of ->{proxy_cache}, |
1408
|
|
|
|
|
|
|
# sticks it in a scalar, then modifies |
1409
|
|
|
|
|
|
|
# it through that scalar. |
1410
|
36
|
|
66
|
|
|
125
|
$$$self{proxy_cache} ||= &fieldhash({}); # & to bypass prototyping |
1411
|
|
|
|
|
|
|
|
1412
|
36
|
100
|
|
|
|
113
|
if(exists $opts{wrapper}) { # special case |
1413
|
1
|
|
|
|
|
4
|
my $pack = $opts{qw/name package/[exists $opts{package}]}; |
1414
|
1
|
|
|
|
|
5
|
$$$self{classes}{$pack} = {wrapper => $opts{wrapper}}; |
1415
|
1
|
|
|
|
|
4
|
return; |
1416
|
|
|
|
|
|
|
} |
1417
|
|
|
|
|
|
|
|
1418
|
35
|
|
|
|
|
37
|
my($pack, $class); |
1419
|
35
|
100
|
|
|
|
69
|
if(exists $opts{package}) { |
1420
|
26
|
|
|
|
|
39
|
$pack = "$opts{package}"; |
1421
|
26
|
100
|
|
|
|
45
|
$class = exists $opts{name} ? $opts{name} : $pack; |
1422
|
|
|
|
|
|
|
} |
1423
|
|
|
|
|
|
|
else { |
1424
|
9
|
|
|
|
|
15
|
$class = $opts{name}; |
1425
|
9
|
|
|
|
|
17
|
$pack = "$class"; |
1426
|
|
|
|
|
|
|
} |
1427
|
|
|
|
|
|
|
|
1428
|
35
|
|
|
|
|
72
|
my %class = ( name => $class ); |
1429
|
35
|
|
|
|
|
117
|
$$$self{classes}{$pack} = $$$self{classes_by_name}{$class} = |
1430
|
|
|
|
|
|
|
\%class; |
1431
|
|
|
|
|
|
|
|
1432
|
35
|
|
|
|
|
52
|
my $unwrap = delete $opts{unwrap}; |
1433
|
|
|
|
|
|
|
|
1434
|
35
|
|
|
|
|
33
|
my ($constructor,$proto,$coderef); |
1435
|
35
|
100
|
|
|
|
66
|
if (exists $opts{constructor}) { |
1436
|
21
|
|
|
|
|
29
|
my $c = $opts{constructor}; |
1437
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
$coderef = ref eq 'CODE' |
1439
|
0
|
|
|
0
|
|
0
|
? sub { $self->upgrade(scalar &$c(@_)) } |
1440
|
21
|
50
|
|
21
|
|
80
|
: sub { $self->upgrade(scalar $pack->$c(@_)) }; |
|
21
|
|
|
|
|
104
|
|
1441
|
|
|
|
|
|
|
} |
1442
|
|
|
|
|
|
|
else { |
1443
|
|
|
|
|
|
|
$coderef = sub { |
1444
|
2
|
|
|
2
|
|
9
|
die JE::Code::add_line_number( |
1445
|
|
|
|
|
|
|
"$class cannot be instantiated"); |
1446
|
14
|
|
|
|
|
60
|
}; |
1447
|
14
|
|
|
|
|
44
|
$constructor = $self->prop($class); |
1448
|
14
|
50
|
66
|
|
|
51
|
defined $constructor and $constructor->typeof ne 'function' |
1449
|
|
|
|
|
|
|
and $constructor = undef; |
1450
|
|
|
|
|
|
|
} |
1451
|
35
|
|
66
|
|
|
275
|
$class{prototype} = $proto = ( $constructor || $self->prop({ |
1452
|
|
|
|
|
|
|
name => $class, |
1453
|
|
|
|
|
|
|
value => $constructor = JE::Object::Function->new({ |
1454
|
|
|
|
|
|
|
name => $class, |
1455
|
|
|
|
|
|
|
scope => $self, |
1456
|
|
|
|
|
|
|
function => $coderef, |
1457
|
|
|
|
|
|
|
function_args => ['args'], |
1458
|
|
|
|
|
|
|
constructor => $coderef, |
1459
|
|
|
|
|
|
|
constructor_args => ['args'], |
1460
|
|
|
|
|
|
|
}), |
1461
|
|
|
|
|
|
|
}) )->prop('prototype'); |
1462
|
|
|
|
|
|
|
|
1463
|
35
|
|
|
|
|
98
|
my $super; |
1464
|
35
|
100
|
|
|
|
68
|
if(exists $opts{isa}) { |
1465
|
3
|
|
|
|
|
4
|
my $isa = $opts{isa}; |
1466
|
|
|
|
|
|
|
$proto->prototype( |
1467
|
|
|
|
|
|
|
!defined $isa || defined blessed $isa |
1468
|
|
|
|
|
|
|
? $isa |
1469
|
3
|
100
|
100
|
|
|
17
|
: do { |
1470
|
1
|
|
|
|
|
1
|
$super = $isa; |
1471
|
1
|
50
|
|
|
|
3
|
defined(my $super_constr = $self->prop($isa)) || |
1472
|
|
|
|
|
|
|
croak("JE::bind_class: The $isa" . |
1473
|
|
|
|
|
|
|
" constructor does not exist"); |
1474
|
1
|
|
|
|
|
3
|
$super_constr->prop('prototype') |
1475
|
|
|
|
|
|
|
} |
1476
|
|
|
|
|
|
|
); |
1477
|
|
|
|
|
|
|
} |
1478
|
|
|
|
|
|
|
|
1479
|
35
|
100
|
|
|
|
69
|
if(exists $opts{methods}) { |
1480
|
8
|
|
|
|
|
11
|
my $methods = $opts{methods}; |
1481
|
8
|
100
|
|
|
|
17
|
if (ref $methods eq 'ARRAY') { for (@$methods) { |
|
2
|
|
|
|
|
4
|
|
1482
|
6
|
|
|
|
|
11
|
my($m, $type) = _split_meth $_; |
1483
|
6
|
100
|
|
|
|
12
|
if (defined $type) { |
1484
|
|
|
|
|
|
|
$proto->new_method( |
1485
|
|
|
|
|
|
|
$m => $unwrap |
1486
|
|
|
|
|
|
|
? sub { |
1487
|
0
|
|
|
0
|
|
0
|
$self->_cast( |
1488
|
|
|
|
|
|
|
scalar shift->value->$m( |
1489
|
|
|
|
|
|
|
$self->_unwrap(@_)), |
1490
|
|
|
|
|
|
|
$type |
1491
|
|
|
|
|
|
|
); |
1492
|
|
|
|
|
|
|
} |
1493
|
|
|
|
|
|
|
: sub { |
1494
|
3
|
|
|
3
|
|
8
|
$self->_cast( |
1495
|
|
|
|
|
|
|
scalar shift->value->$m(@_), |
1496
|
|
|
|
|
|
|
$type |
1497
|
|
|
|
|
|
|
); |
1498
|
|
|
|
|
|
|
} |
1499
|
3
|
50
|
|
|
|
19
|
); |
1500
|
|
|
|
|
|
|
}else { |
1501
|
|
|
|
|
|
|
$proto->new_method( |
1502
|
|
|
|
|
|
|
$m => $unwrap |
1503
|
0
|
|
|
0
|
|
0
|
? sub { shift->value->$m( |
1504
|
|
|
|
|
|
|
$self->_unwrap(@_)) } |
1505
|
3
|
|
|
3
|
|
13
|
: sub { shift->value->$m(@_) }, |
1506
|
3
|
50
|
|
|
|
23
|
); |
1507
|
|
|
|
|
|
|
} |
1508
|
|
|
|
|
|
|
}} else { # it'd better be a hash! |
1509
|
6
|
|
|
|
|
26
|
while( my($name, $m) = each %$methods) { |
1510
|
14
|
100
|
|
|
|
30
|
if(ref $m eq 'CODE') { |
1511
|
|
|
|
|
|
|
$proto->new_method( |
1512
|
|
|
|
|
|
|
$name => $unwrap |
1513
|
|
|
|
|
|
|
? sub { |
1514
|
0
|
|
|
0
|
|
0
|
&$m($self->_unwrap(@_)) |
1515
|
|
|
|
|
|
|
} |
1516
|
|
|
|
|
|
|
: sub { |
1517
|
4
|
|
|
4
|
|
14
|
&$m($_[0]->value,@_[1..$#_]) |
1518
|
|
|
|
|
|
|
} |
1519
|
8
|
50
|
|
|
|
60
|
); |
1520
|
|
|
|
|
|
|
} else { |
1521
|
6
|
|
|
|
|
12
|
my ($method, $type) = _split_meth $m; |
1522
|
|
|
|
|
|
|
$proto->new_method( |
1523
|
|
|
|
|
|
|
$name => defined $type |
1524
|
|
|
|
|
|
|
? $unwrap |
1525
|
|
|
|
|
|
|
? sub { |
1526
|
0
|
|
|
0
|
|
0
|
$self->_cast( |
1527
|
|
|
|
|
|
|
scalar shift->value->$method( |
1528
|
|
|
|
|
|
|
$self->_unwrap(@_)), |
1529
|
|
|
|
|
|
|
$type |
1530
|
|
|
|
|
|
|
); |
1531
|
|
|
|
|
|
|
} |
1532
|
|
|
|
|
|
|
: sub { |
1533
|
3
|
|
|
3
|
|
7
|
$self->_cast( |
1534
|
|
|
|
|
|
|
scalar shift->value->$method(@_), |
1535
|
|
|
|
|
|
|
$type |
1536
|
|
|
|
|
|
|
); |
1537
|
|
|
|
|
|
|
} |
1538
|
|
|
|
|
|
|
: $unwrap |
1539
|
0
|
|
|
0
|
|
0
|
? sub { shift->value->$m( |
1540
|
|
|
|
|
|
|
$self->_unwrap(@_)) } |
1541
|
3
|
|
|
3
|
|
14
|
: sub { shift->value->$m(@_) }, |
1542
|
6
|
50
|
|
|
|
55
|
); |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
} |
1544
|
|
|
|
|
|
|
}} |
1545
|
|
|
|
|
|
|
} |
1546
|
|
|
|
|
|
|
|
1547
|
35
|
100
|
|
|
|
88
|
if(exists $opts{static_methods}) { |
1548
|
5
|
|
|
|
|
9
|
my $methods = $opts{static_methods}; |
1549
|
5
|
100
|
|
|
|
13
|
if (ref $methods eq 'ARRAY') { for (@$methods) { |
|
2
|
|
|
|
|
11
|
|
1550
|
6
|
|
|
|
|
15
|
my($m, $type) = _split_meth $_; |
1551
|
|
|
|
|
|
|
$constructor->new_function( |
1552
|
|
|
|
|
|
|
$m => defined $type |
1553
|
|
|
|
|
|
|
? $unwrap |
1554
|
0
|
|
|
0
|
|
0
|
? sub { $self->_cast( |
1555
|
|
|
|
|
|
|
scalar $pack->$m( |
1556
|
|
|
|
|
|
|
$self->_unwrap(@_)), $type |
1557
|
|
|
|
|
|
|
) } |
1558
|
3
|
|
|
3
|
|
20
|
: sub { $self->_cast( |
1559
|
|
|
|
|
|
|
scalar $pack->$m(@_), $type |
1560
|
|
|
|
|
|
|
) } |
1561
|
|
|
|
|
|
|
: $unwrap |
1562
|
0
|
|
|
0
|
|
0
|
? sub { $pack->$m( |
1563
|
|
|
|
|
|
|
$self->_unwrap(@_)) } |
1564
|
3
|
|
|
3
|
|
24
|
: sub { $pack->$m(@_) } |
1565
|
6
|
50
|
|
|
|
57
|
); |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
# new_function makes the functions enumerable, |
1567
|
|
|
|
|
|
|
# unlike new_method. This code is here to make |
1568
|
|
|
|
|
|
|
# things consistent. I'll delete it if someone |
1569
|
|
|
|
|
|
|
# convinces me otherwise. (I can't make |
1570
|
|
|
|
|
|
|
# up my mind.) |
1571
|
6
|
|
|
|
|
22
|
$constructor->prop({ |
1572
|
|
|
|
|
|
|
name => $m, dontenum => 1 |
1573
|
|
|
|
|
|
|
}); |
1574
|
|
|
|
|
|
|
}} else { # it'd better be a hash! |
1575
|
3
|
|
|
|
|
11
|
while( my($name, $m) = each %$methods) { |
1576
|
8
|
100
|
|
|
|
14
|
if(ref $m eq 'CODE') { |
1577
|
|
|
|
|
|
|
$constructor->new_function( |
1578
|
|
|
|
|
|
|
$name => $unwrap |
1579
|
|
|
|
|
|
|
? sub { |
1580
|
0
|
|
|
0
|
|
0
|
@_ = $self->_unwrap(@_); |
1581
|
0
|
|
|
|
|
0
|
unshift @_, $pack; |
1582
|
0
|
|
|
|
|
0
|
goto $m; |
1583
|
|
|
|
|
|
|
} |
1584
|
|
|
|
|
|
|
: sub { |
1585
|
2
|
|
|
2
|
|
6
|
unshift @_, $pack; |
1586
|
2
|
|
|
|
|
9
|
goto $m; |
1587
|
|
|
|
|
|
|
} |
1588
|
2
|
50
|
|
|
|
14
|
); |
1589
|
|
|
|
|
|
|
} else { |
1590
|
6
|
|
|
|
|
15
|
($m, my $type) = _split_meth $m; |
1591
|
|
|
|
|
|
|
$constructor->new_function( |
1592
|
|
|
|
|
|
|
$name => defined $type |
1593
|
3
|
|
|
3
|
|
23
|
? sub { $self->_cast( |
1594
|
|
|
|
|
|
|
scalar $pack->$m, |
1595
|
|
|
|
|
|
|
$type |
1596
|
|
|
|
|
|
|
) } |
1597
|
|
|
|
|
|
|
: $unwrap |
1598
|
0
|
|
|
0
|
|
0
|
? sub { $pack->$m( |
1599
|
|
|
|
|
|
|
$self->_unwrap(@_)) } |
1600
|
3
|
|
|
3
|
|
22
|
: sub { $pack->$m(@_) }, |
1601
|
6
|
50
|
|
|
|
47
|
); |
|
|
100
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
} |
1603
|
|
|
|
|
|
|
# new_function makes the functions enumerable, |
1604
|
|
|
|
|
|
|
# unlike new_method. This code is here to make |
1605
|
|
|
|
|
|
|
# things consistent. I'll delete it if someone |
1606
|
|
|
|
|
|
|
# convinces me otherwise. (I can't make |
1607
|
|
|
|
|
|
|
# up my mind.) |
1608
|
8
|
|
|
|
|
27
|
$constructor->prop({ |
1609
|
|
|
|
|
|
|
name => $name, dontenum => 1 |
1610
|
|
|
|
|
|
|
}); |
1611
|
|
|
|
|
|
|
}} |
1612
|
|
|
|
|
|
|
} |
1613
|
|
|
|
|
|
|
|
1614
|
35
|
|
|
|
|
60
|
for(qw/to_primitive to_string to_number/) { |
1615
|
105
|
100
|
|
|
|
229
|
exists $opts{$_} and $class{$_} = $opts{$_} |
1616
|
|
|
|
|
|
|
} |
1617
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
# The properties enumerated by the 'props' option need to be made |
1619
|
|
|
|
|
|
|
# instance properties, since assignment never falls through to the |
1620
|
|
|
|
|
|
|
# prototype, and a fetch routine is passed the property's actual |
1621
|
|
|
|
|
|
|
# owner; i.e., the prototype, if it is an inherited property. So |
1622
|
|
|
|
|
|
|
# we'll make a list of argument lists which &JE::Object::Proxy::new |
1623
|
|
|
|
|
|
|
# will take care of passing to each object's prop method. |
1624
|
35
|
|
|
|
|
36
|
{ my %props; |
|
35
|
|
|
|
|
30
|
|
1625
|
35
|
100
|
|
|
|
74
|
if(exists $opts{props}) { |
1626
|
11
|
|
|
|
|
15
|
my $props = $opts{props}; |
1627
|
11
|
|
|
|
|
15
|
$class{props} = \%props; |
1628
|
11
|
100
|
|
|
|
26
|
if (ref $props eq 'ARRAY') { |
1629
|
2
|
|
|
|
|
5
|
for(@$props) { |
1630
|
6
|
|
|
|
|
10
|
my ($p,$type) = _split_meth $_; |
1631
|
|
|
|
|
|
|
$props{$p} = [ |
1632
|
|
|
|
|
|
|
fetch => defined $type |
1633
|
|
|
|
|
|
|
? sub { |
1634
|
3
|
|
|
3
|
|
12
|
$self->_cast( |
1635
|
|
|
|
|
|
|
scalar $_[0]->value->$p, $type |
1636
|
|
|
|
|
|
|
) |
1637
|
|
|
|
|
|
|
} |
1638
|
|
|
|
|
|
|
: sub { |
1639
|
4
|
|
|
4
|
|
15
|
$self->upgrade(scalar $_[0]->value->$p) |
1640
|
|
|
|
|
|
|
}, |
1641
|
|
|
|
|
|
|
store => $unwrap |
1642
|
0
|
|
|
0
|
|
0
|
? sub { $_[0]->value->$p( |
1643
|
|
|
|
|
|
|
$self->_unwrap($_[1])) } |
1644
|
2
|
|
|
2
|
|
6
|
: sub { $_[0]->value->$p($_[1]) }, |
1645
|
6
|
100
|
|
|
|
48
|
]; |
|
|
50
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
} |
1647
|
|
|
|
|
|
|
} else { # it'd better be a hash! |
1648
|
9
|
|
|
|
|
34
|
while( my($name, $p) = each %$props) { |
1649
|
20
|
|
|
|
|
15
|
my @prop_args; |
1650
|
20
|
100
|
|
|
|
37
|
if (ref $p eq 'HASH') { |
1651
|
11
|
100
|
|
|
|
19
|
if(exists $$p{fetch}) { |
1652
|
9
|
|
|
|
|
10
|
my $fetch = $$p{fetch}; |
1653
|
|
|
|
|
|
|
@prop_args = ( fetch => |
1654
|
|
|
|
|
|
|
ref $fetch eq 'CODE' |
1655
|
3
|
|
|
3
|
|
10
|
? sub { $self->upgrade( |
1656
|
|
|
|
|
|
|
scalar &$fetch($_[0]->value) |
1657
|
|
|
|
|
|
|
) } |
1658
|
9
|
100
|
|
|
|
20
|
: do { |
1659
|
7
|
|
|
|
|
11
|
my($f,$t) = _split_meth $fetch; |
1660
|
3
|
|
|
3
|
|
8
|
defined $t ? sub { $self->_cast( |
1661
|
|
|
|
|
|
|
scalar shift->value->$f, $t |
1662
|
|
|
|
|
|
|
) } |
1663
|
5
|
|
|
5
|
|
14
|
: sub { $self->upgrade( |
1664
|
|
|
|
|
|
|
scalar shift->value->$fetch |
1665
|
|
|
|
|
|
|
) } |
1666
|
7
|
100
|
|
|
|
34
|
} |
1667
|
|
|
|
|
|
|
); |
1668
|
|
|
|
|
|
|
} |
1669
|
2
|
|
|
|
|
6
|
else { @prop_args = |
1670
|
|
|
|
|
|
|
(value => $self->undefined); |
1671
|
|
|
|
|
|
|
} |
1672
|
11
|
100
|
|
|
|
21
|
if(exists $$p{store}) { |
1673
|
5
|
|
|
|
|
5
|
my $store = $$p{store}; |
1674
|
|
|
|
|
|
|
push @prop_args, ( store => |
1675
|
|
|
|
|
|
|
ref $store eq 'CODE' |
1676
|
|
|
|
|
|
|
? $unwrap |
1677
|
|
|
|
|
|
|
? sub { |
1678
|
0
|
|
|
0
|
|
0
|
&$store($_[0]->value, |
1679
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1680
|
|
|
|
|
|
|
} |
1681
|
|
|
|
|
|
|
: sub { |
1682
|
2
|
|
|
2
|
|
7
|
&$store($_[0]->value, $_[1]) |
1683
|
|
|
|
|
|
|
} |
1684
|
|
|
|
|
|
|
: $unwrap |
1685
|
|
|
|
|
|
|
? sub { |
1686
|
0
|
|
|
0
|
|
0
|
$_[0]->value->$store( |
1687
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1688
|
|
|
|
|
|
|
} |
1689
|
|
|
|
|
|
|
: sub { |
1690
|
3
|
|
|
3
|
|
11
|
$_[0]->value->$store($_[1]) |
1691
|
|
|
|
|
|
|
} |
1692
|
5
|
50
|
|
|
|
23
|
); |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
} |
1694
|
|
|
|
|
|
|
else { |
1695
|
6
|
|
|
|
|
9
|
push @prop_args, readonly => 1; |
1696
|
|
|
|
|
|
|
} |
1697
|
|
|
|
|
|
|
} |
1698
|
|
|
|
|
|
|
else { |
1699
|
9
|
100
|
|
|
|
19
|
if(ref $p eq 'CODE') { |
1700
|
|
|
|
|
|
|
@prop_args = ( |
1701
|
3
|
|
|
3
|
|
12
|
fetch => sub { $self->upgrade( |
1702
|
|
|
|
|
|
|
scalar &$p($_[0]->value) |
1703
|
|
|
|
|
|
|
) }, |
1704
|
|
|
|
|
|
|
store => $unwrap |
1705
|
|
|
|
|
|
|
? sub { |
1706
|
0
|
|
|
0
|
|
0
|
&$p( |
1707
|
|
|
|
|
|
|
scalar $_[0]->value, |
1708
|
|
|
|
|
|
|
$self->_unwrap($_[1]) |
1709
|
|
|
|
|
|
|
) |
1710
|
|
|
|
|
|
|
} |
1711
|
|
|
|
|
|
|
: sub { |
1712
|
2
|
|
|
2
|
|
7
|
&$p( |
1713
|
|
|
|
|
|
|
scalar $_[0]->value, $_[1] |
1714
|
|
|
|
|
|
|
) |
1715
|
|
|
|
|
|
|
}, |
1716
|
2
|
50
|
|
|
|
13
|
); |
1717
|
|
|
|
|
|
|
}else{ |
1718
|
7
|
|
|
|
|
10
|
($p, my $t) = _split_meth($p); |
1719
|
|
|
|
|
|
|
@prop_args = ( |
1720
|
|
|
|
|
|
|
fetch => defined $t |
1721
|
3
|
|
|
3
|
|
9
|
? sub { $self->_cast( |
1722
|
|
|
|
|
|
|
scalar $_[0]->value->$p, $t |
1723
|
|
|
|
|
|
|
) } |
1724
|
6
|
|
|
6
|
|
20
|
: sub { $self->upgrade( |
1725
|
|
|
|
|
|
|
scalar $_[0]->value->$p |
1726
|
|
|
|
|
|
|
) }, |
1727
|
|
|
|
|
|
|
store => $unwrap |
1728
|
|
|
|
|
|
|
? sub { |
1729
|
0
|
|
|
0
|
|
0
|
$_[0]->value->$p( |
1730
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1731
|
|
|
|
|
|
|
} |
1732
|
|
|
|
|
|
|
: sub { |
1733
|
2
|
|
|
2
|
|
6
|
$_[0]->value->$p($_[1]) |
1734
|
|
|
|
|
|
|
}, |
1735
|
7
|
100
|
|
|
|
60
|
); |
|
|
50
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
} |
1737
|
|
|
|
|
|
|
} |
1738
|
20
|
|
|
|
|
68
|
$props{$name} = \@prop_args; |
1739
|
|
|
|
|
|
|
}} |
1740
|
|
|
|
|
|
|
} |
1741
|
35
|
100
|
|
|
|
76
|
if(defined $super){ |
1742
|
1
|
|
50
|
|
|
6
|
$class{props} ||= \%props; |
1743
|
|
|
|
|
|
|
{ |
1744
|
1
|
|
50
|
|
|
1
|
my $super_props = |
|
1
|
|
|
|
|
6
|
|
1745
|
|
|
|
|
|
|
$$$self{classes_by_name}{$super}{props} |
1746
|
|
|
|
|
|
|
|| last; |
1747
|
0
|
|
|
|
|
0
|
for (keys %$super_props) { |
1748
|
0
|
0
|
|
|
|
0
|
exists $props{$_} or |
1749
|
|
|
|
|
|
|
$props{$_} = $$super_props{$_} |
1750
|
|
|
|
|
|
|
} |
1751
|
|
|
|
|
|
|
} |
1752
|
|
|
|
|
|
|
}} |
1753
|
|
|
|
|
|
|
|
1754
|
35
|
100
|
|
|
|
75
|
if(exists $opts{static_props}) { |
1755
|
11
|
|
|
|
|
15
|
my $props = $opts{static_props}; |
1756
|
11
|
100
|
|
|
|
19
|
if (ref $props eq 'ARRAY') { for (@$props) { |
|
2
|
|
|
|
|
4
|
|
1757
|
6
|
|
|
|
|
8
|
my($p,$t) = _split_meth $_; |
1758
|
|
|
|
|
|
|
$constructor->prop({ |
1759
|
|
|
|
|
|
|
name => $p, |
1760
|
|
|
|
|
|
|
fetch => defined $t |
1761
|
3
|
|
|
3
|
|
20
|
? sub { $self->_cast( |
1762
|
|
|
|
|
|
|
scalar $pack->$p, $t |
1763
|
|
|
|
|
|
|
) } |
1764
|
3
|
|
|
3
|
|
17
|
: sub { $self->upgrade( |
1765
|
|
|
|
|
|
|
scalar $pack->$p |
1766
|
|
|
|
|
|
|
) }, |
1767
|
0
|
|
|
0
|
|
0
|
store => $unwrap |
1768
|
|
|
|
|
|
|
? sub {$pack->$p($self->_unwrap($_[1]))} |
1769
|
2
|
|
|
2
|
|
11
|
: sub { $pack->$p($_[1]) }, |
1770
|
6
|
100
|
|
|
|
50
|
}); |
|
|
50
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
}} else { # it'd better be a hash! |
1772
|
9
|
|
|
|
|
28
|
while( my($name, $p) = each %$props) { |
1773
|
19
|
|
|
|
|
20
|
my @prop_args; |
1774
|
19
|
100
|
|
|
|
28
|
if (ref $p eq 'HASH') { |
1775
|
11
|
100
|
|
|
|
18
|
if(exists $$p{fetch}) { |
1776
|
9
|
|
|
|
|
11
|
my $fetch = $$p{fetch}; |
1777
|
|
|
|
|
|
|
@prop_args = ( fetch => |
1778
|
|
|
|
|
|
|
ref $fetch eq 'CODE' |
1779
|
|
|
|
|
|
|
? sub { |
1780
|
2
|
|
|
2
|
|
7
|
$self->upgrade( |
1781
|
|
|
|
|
|
|
scalar &$fetch($pack)) |
1782
|
|
|
|
|
|
|
} |
1783
|
9
|
100
|
|
|
|
18
|
: do { |
1784
|
7
|
|
|
|
|
11
|
my($f,$t) = _split_meth $fetch; |
1785
|
|
|
|
|
|
|
defined $t ? sub { |
1786
|
1
|
|
|
1
|
|
78
|
$self->_cast( |
1787
|
|
|
|
|
|
|
scalar $pack->$f,$t) |
1788
|
|
|
|
|
|
|
} |
1789
|
|
|
|
|
|
|
: sub { |
1790
|
3
|
|
|
3
|
|
14
|
$self->upgrade( |
1791
|
|
|
|
|
|
|
scalar $pack->$f) |
1792
|
|
|
|
|
|
|
} |
1793
|
7
|
100
|
|
|
|
33
|
} |
1794
|
|
|
|
|
|
|
); |
1795
|
|
|
|
|
|
|
} |
1796
|
2
|
|
|
|
|
5
|
else { @prop_args = |
1797
|
|
|
|
|
|
|
(value => $self->undefined); |
1798
|
|
|
|
|
|
|
} |
1799
|
11
|
100
|
|
|
|
22
|
if(exists $$p{store}) { |
1800
|
5
|
|
|
|
|
7
|
my $store = $$p{store}; |
1801
|
|
|
|
|
|
|
push @prop_args, ( store => |
1802
|
|
|
|
|
|
|
ref $store eq 'CODE' |
1803
|
|
|
|
|
|
|
? $unwrap |
1804
|
|
|
|
|
|
|
? sub { |
1805
|
0
|
|
|
0
|
|
0
|
&$store($pack, |
1806
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1807
|
|
|
|
|
|
|
} |
1808
|
|
|
|
|
|
|
: sub { |
1809
|
2
|
|
|
2
|
|
8
|
&$store($pack, $_[1]) |
1810
|
|
|
|
|
|
|
} |
1811
|
|
|
|
|
|
|
: $unwrap |
1812
|
|
|
|
|
|
|
? sub { |
1813
|
0
|
|
|
0
|
|
0
|
$pack->$store( |
1814
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1815
|
|
|
|
|
|
|
} |
1816
|
|
|
|
|
|
|
: sub { |
1817
|
3
|
|
|
3
|
|
15
|
$pack->$store($_[1]) |
1818
|
|
|
|
|
|
|
} |
1819
|
5
|
50
|
|
|
|
20
|
); |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
} |
1821
|
|
|
|
|
|
|
else { |
1822
|
6
|
|
|
|
|
8
|
push @prop_args, readonly => 1; |
1823
|
|
|
|
|
|
|
} |
1824
|
|
|
|
|
|
|
} |
1825
|
|
|
|
|
|
|
else { |
1826
|
8
|
100
|
|
|
|
14
|
if(ref $p eq 'CODE') { |
1827
|
|
|
|
|
|
|
@prop_args = ( |
1828
|
|
|
|
|
|
|
fetch => sub { |
1829
|
2
|
|
|
2
|
|
6
|
$self->upgrade( |
1830
|
|
|
|
|
|
|
scalar &$p($pack)) |
1831
|
|
|
|
|
|
|
}, |
1832
|
|
|
|
|
|
|
store => $unwrap |
1833
|
|
|
|
|
|
|
? sub { |
1834
|
0
|
|
|
0
|
|
0
|
&$p($pack, |
1835
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1836
|
|
|
|
|
|
|
} |
1837
|
|
|
|
|
|
|
: sub { |
1838
|
2
|
|
|
2
|
|
10
|
&$p($pack, $_[1]) |
1839
|
|
|
|
|
|
|
}, |
1840
|
2
|
50
|
|
|
|
12
|
); |
1841
|
|
|
|
|
|
|
} else { |
1842
|
6
|
|
|
|
|
10
|
($p, my $t) = _split_meth $p; |
1843
|
|
|
|
|
|
|
@prop_args = ( |
1844
|
|
|
|
|
|
|
fetch => defined $t |
1845
|
|
|
|
|
|
|
? sub { |
1846
|
3
|
|
|
3
|
|
19
|
$self->_cast( |
1847
|
|
|
|
|
|
|
scalar $pack->$p,$t) |
1848
|
|
|
|
|
|
|
} |
1849
|
|
|
|
|
|
|
: sub { |
1850
|
3
|
|
|
3
|
|
16
|
$self->upgrade( |
1851
|
|
|
|
|
|
|
scalar $pack->$p) |
1852
|
|
|
|
|
|
|
}, |
1853
|
|
|
|
|
|
|
store => $unwrap |
1854
|
|
|
|
|
|
|
? sub { |
1855
|
0
|
|
|
0
|
|
0
|
$pack->$p( |
1856
|
|
|
|
|
|
|
$self->_unwrap($_[1])) |
1857
|
|
|
|
|
|
|
} |
1858
|
|
|
|
|
|
|
: sub { |
1859
|
2
|
|
|
2
|
|
12
|
$pack->$p($_[1]) |
1860
|
|
|
|
|
|
|
}, |
1861
|
6
|
100
|
|
|
|
40
|
); |
|
|
50
|
|
|
|
|
|
1862
|
|
|
|
|
|
|
} |
1863
|
|
|
|
|
|
|
} |
1864
|
19
|
|
|
|
|
64
|
$constructor->prop({name => $name, @prop_args}); |
1865
|
|
|
|
|
|
|
}} |
1866
|
|
|
|
|
|
|
} |
1867
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
# ~~~ needs to be made more elaborate |
1869
|
|
|
|
|
|
|
# ~~~ for later: exists $opts{keys} and $class{keys} = $$opts{keys}; |
1870
|
|
|
|
|
|
|
|
1871
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
|
1873
|
|
|
|
|
|
|
# $class{hash}{store} will be a coderef that returns true or false, |
1874
|
|
|
|
|
|
|
# depending on whether it was able to write the property. With two- |
1875
|
|
|
|
|
|
|
# way hash bindings, it will always return true |
1876
|
|
|
|
|
|
|
|
1877
|
35
|
100
|
|
|
|
68
|
if($opts{hash}) { |
1878
|
3
|
50
|
33
|
|
|
24
|
if(!ref $opts{hash} # ) { |
1879
|
|
|
|
|
|
|
#if( |
1880
|
|
|
|
|
|
|
&& $opts{hash} =~ /^(?:1|(2))/) { |
1881
|
|
|
|
|
|
|
$class{hash} = { |
1882
|
8
|
100
|
|
8
|
|
24
|
fetch => sub { exists $_[0]{$_[1]} |
1883
|
|
|
|
|
|
|
? $self->upgrade( |
1884
|
|
|
|
|
|
|
$_[0]{$_[1]}) |
1885
|
|
|
|
|
|
|
: undef |
1886
|
|
|
|
|
|
|
}, |
1887
|
|
|
|
|
|
|
store => $1 # two-way? |
1888
|
1
|
|
|
1
|
|
3
|
? sub { $_[0]{$_[1]}=$_[2]; 1 } |
|
1
|
|
|
|
|
6
|
|
1889
|
|
|
|
|
|
|
: sub { |
1890
|
1
|
50
|
|
1
|
|
13
|
exists $_[0]{$_[1]} and |
1891
|
|
|
|
|
|
|
($_[0]{$_[1]}=$_[2], 1) |
1892
|
|
|
|
|
|
|
}, |
1893
|
3
|
100
|
|
|
|
26
|
}; |
1894
|
3
|
|
50
|
0
|
|
20
|
$class{keys} ||= sub { keys %{$_[0]} }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1895
|
|
|
|
|
|
|
} |
1896
|
0
|
|
|
|
|
0
|
else { croak |
1897
|
|
|
|
|
|
|
"Invalid value for the 'hash' option: $opts{hash}"; |
1898
|
|
|
|
|
|
|
} |
1899
|
|
|
|
|
|
|
|
1900
|
|
|
|
|
|
|
=begin comment |
1901
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
# I haven't yet figured out a logical way for this to work: |
1903
|
|
|
|
|
|
|
|
1904
|
|
|
|
|
|
|
else { # method name |
1905
|
|
|
|
|
|
|
my $m = $opts{hash}; |
1906
|
|
|
|
|
|
|
$class{hash} = { |
1907
|
|
|
|
|
|
|
fetch => sub { |
1908
|
|
|
|
|
|
|
$self->_upgr_def( |
1909
|
|
|
|
|
|
|
$_[0]->value->$m($_[1]) |
1910
|
|
|
|
|
|
|
) |
1911
|
|
|
|
|
|
|
}, |
1912
|
|
|
|
|
|
|
store => sub { |
1913
|
|
|
|
|
|
|
my $wrappee = shift->value; |
1914
|
|
|
|
|
|
|
defined $wrappee->$m($_[0]) && |
1915
|
|
|
|
|
|
|
($wrappee->$m(@_), 1) |
1916
|
|
|
|
|
|
|
}, |
1917
|
|
|
|
|
|
|
}; |
1918
|
|
|
|
|
|
|
} |
1919
|
|
|
|
|
|
|
} elsif (ref $opts{hash} eq 'CODE') { |
1920
|
|
|
|
|
|
|
my $cref = $opts{hash}; |
1921
|
|
|
|
|
|
|
$class{hash} = { |
1922
|
|
|
|
|
|
|
fetch => sub { |
1923
|
|
|
|
|
|
|
$self->_upgr_def( |
1924
|
|
|
|
|
|
|
&$cref($_[0]->value, $_[1]) |
1925
|
|
|
|
|
|
|
) |
1926
|
|
|
|
|
|
|
}, |
1927
|
|
|
|
|
|
|
store => sub { |
1928
|
|
|
|
|
|
|
my $wrappee = shift->value; |
1929
|
|
|
|
|
|
|
defined &$cref($wrappee, $_[0]) && |
1930
|
|
|
|
|
|
|
(&$cref($wrappee, @_), 1) |
1931
|
|
|
|
|
|
|
}, |
1932
|
|
|
|
|
|
|
}; |
1933
|
|
|
|
|
|
|
} else { # it'd better be a hash! |
1934
|
|
|
|
|
|
|
my $opt = $opts{hash_elem}; |
1935
|
|
|
|
|
|
|
if(exists $$opt{fetch}) { |
1936
|
|
|
|
|
|
|
my $fetch = $$opt{fetch}; |
1937
|
|
|
|
|
|
|
$class{hash}{fetch} = |
1938
|
|
|
|
|
|
|
ref $fetch eq 'CODE' |
1939
|
|
|
|
|
|
|
? sub { $self-> _upgr_def( |
1940
|
|
|
|
|
|
|
&$fetch($_[0]->value, $_[1]) |
1941
|
|
|
|
|
|
|
) } |
1942
|
|
|
|
|
|
|
: sub { $self-> _upgr_def( |
1943
|
|
|
|
|
|
|
shift->value->$fetch(shift) |
1944
|
|
|
|
|
|
|
) } |
1945
|
|
|
|
|
|
|
; |
1946
|
|
|
|
|
|
|
} |
1947
|
|
|
|
|
|
|
if(exists $$opt{store}) { |
1948
|
|
|
|
|
|
|
my $store = $$opt{store}; |
1949
|
|
|
|
|
|
|
$class{hash}{store} = |
1950
|
|
|
|
|
|
|
ref $store eq 'CODE' |
1951
|
|
|
|
|
|
|
? sub { |
1952
|
|
|
|
|
|
|
my $wrappee = shift->value; |
1953
|
|
|
|
|
|
|
defined &$store($wrappee, $_[0]) |
1954
|
|
|
|
|
|
|
and &$store($wrappee, @_), 1 |
1955
|
|
|
|
|
|
|
} |
1956
|
|
|
|
|
|
|
: sub { |
1957
|
|
|
|
|
|
|
my $wrappee = shift->value; |
1958
|
|
|
|
|
|
|
defined $wrappee->$store($_[0]) |
1959
|
|
|
|
|
|
|
and &$store($wrappee, @_), 1 |
1960
|
|
|
|
|
|
|
$_[0]->value->$store(@_[1,2]) |
1961
|
|
|
|
|
|
|
} |
1962
|
|
|
|
|
|
|
; |
1963
|
|
|
|
|
|
|
} |
1964
|
|
|
|
|
|
|
} |
1965
|
|
|
|
|
|
|
|
1966
|
|
|
|
|
|
|
=end comment |
1967
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
=cut |
1969
|
|
|
|
|
|
|
|
1970
|
|
|
|
|
|
|
} |
1971
|
|
|
|
|
|
|
|
1972
|
35
|
100
|
|
|
|
74
|
if($opts{array}) { |
1973
|
3
|
50
|
|
|
|
17
|
if($opts{array} =~ /^(?:1|(2))/) { |
1974
|
|
|
|
|
|
|
$class{array} = { |
1975
|
11
|
100
|
|
11
|
|
13
|
fetch => sub { $_[1] < @{$_[0]} |
|
11
|
|
|
|
|
38
|
|
1976
|
|
|
|
|
|
|
? $self->upgrade( |
1977
|
|
|
|
|
|
|
$_[0][$_[1]]) |
1978
|
|
|
|
|
|
|
: undef |
1979
|
|
|
|
|
|
|
}, |
1980
|
|
|
|
|
|
|
store => $1 # two-way? |
1981
|
1
|
|
|
1
|
|
4
|
? sub { $_[0][$_[1]]=$_[2]; 1 } |
|
1
|
|
|
|
|
6
|
|
1982
|
|
|
|
|
|
|
: sub { |
1983
|
1
|
50
|
|
1
|
|
2
|
$_[1] < @{$_[0]} and |
|
1
|
|
|
|
|
8
|
|
1984
|
|
|
|
|
|
|
($_[0]{$_[1]}=$_[2], 1) |
1985
|
|
|
|
|
|
|
}, |
1986
|
3
|
100
|
|
|
|
31
|
}; |
1987
|
|
|
|
|
|
|
} |
1988
|
0
|
|
|
|
|
0
|
else { croak |
1989
|
|
|
|
|
|
|
"Invalid value for the 'array' option: $opts{array}"; |
1990
|
|
|
|
|
|
|
} |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
=begin comment |
1993
|
|
|
|
|
|
|
|
1994
|
|
|
|
|
|
|
} elsif (exists $opts{array_elem}) { |
1995
|
|
|
|
|
|
|
if (!ref $opts{array_elem}) { |
1996
|
|
|
|
|
|
|
my $m = $opts{array_elem}; |
1997
|
|
|
|
|
|
|
$class{array} = { |
1998
|
|
|
|
|
|
|
fetch => sub { |
1999
|
|
|
|
|
|
|
$self->upgrade( |
2000
|
|
|
|
|
|
|
$_[0]->value->$m($_[1]) |
2001
|
|
|
|
|
|
|
) |
2002
|
|
|
|
|
|
|
}, |
2003
|
|
|
|
|
|
|
store => sub { $_[0]->value->$m(@_[1,2]) }, |
2004
|
|
|
|
|
|
|
}; |
2005
|
|
|
|
|
|
|
} else { # it'd better be a hash! |
2006
|
|
|
|
|
|
|
my $opt = $opts{array_elem}; |
2007
|
|
|
|
|
|
|
if(exists $$opt{fetch}) { |
2008
|
|
|
|
|
|
|
my $fetch = $$opt{fetch}; |
2009
|
|
|
|
|
|
|
$class{array}{fetch} = |
2010
|
|
|
|
|
|
|
ref $fetch eq 'CODE' |
2011
|
|
|
|
|
|
|
? sub { $self->upgrade( |
2012
|
|
|
|
|
|
|
&$fetch($_[0]->value, $_[1]) |
2013
|
|
|
|
|
|
|
) } |
2014
|
|
|
|
|
|
|
: sub { $self->upgrade( |
2015
|
|
|
|
|
|
|
shift->value->$fetch(shift) |
2016
|
|
|
|
|
|
|
) } |
2017
|
|
|
|
|
|
|
; |
2018
|
|
|
|
|
|
|
} |
2019
|
|
|
|
|
|
|
if(exists $$opt{store}) { |
2020
|
|
|
|
|
|
|
my $store = $$opt{store}; |
2021
|
|
|
|
|
|
|
$class{array}{store} = |
2022
|
|
|
|
|
|
|
ref $store eq 'CODE' |
2023
|
|
|
|
|
|
|
? sub { |
2024
|
|
|
|
|
|
|
&$store($_[0]->value, @_[1,2]) |
2025
|
|
|
|
|
|
|
} |
2026
|
|
|
|
|
|
|
: sub { |
2027
|
|
|
|
|
|
|
$_[0]->value->$store(@_[1,2]) |
2028
|
|
|
|
|
|
|
} |
2029
|
|
|
|
|
|
|
; |
2030
|
|
|
|
|
|
|
} |
2031
|
|
|
|
|
|
|
} |
2032
|
|
|
|
|
|
|
|
2033
|
|
|
|
|
|
|
=end comment |
2034
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
=cut |
2036
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
} |
2038
|
|
|
|
|
|
|
|
2039
|
35
|
|
|
|
|
82
|
weaken $self; # we've got closures |
2040
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
return # nothing |
2042
|
35
|
|
|
|
|
132
|
} |
2043
|
|
|
|
|
|
|
|
2044
|
|
|
|
|
|
|
=over |
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
=item $j->new_parser |
2047
|
|
|
|
|
|
|
|
2048
|
|
|
|
|
|
|
This returns a parser object (see L) which allows you to |
2049
|
|
|
|
|
|
|
customise the way statements are parsed and executed (only partially |
2050
|
|
|
|
|
|
|
implemented). |
2051
|
|
|
|
|
|
|
|
2052
|
|
|
|
|
|
|
=cut |
2053
|
|
|
|
|
|
|
|
2054
|
|
|
|
|
|
|
sub new_parser { |
2055
|
1
|
|
|
1
|
1
|
5
|
JE::Parser->new(shift); |
2056
|
|
|
|
|
|
|
} |
2057
|
|
|
|
|
|
|
|
2058
|
|
|
|
|
|
|
|
2059
|
|
|
|
|
|
|
|
2060
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
=item $j->prototype_for( $class_name ) |
2062
|
|
|
|
|
|
|
|
2063
|
|
|
|
|
|
|
=item $j->prototype_for( $class_name, $new_val ) |
2064
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
Mostly for internal use, this method is used to store/retrieve the |
2066
|
|
|
|
|
|
|
prototype objects used by JS's built-in data types. The class name should |
2067
|
|
|
|
|
|
|
be 'String', 'Number', etc., but you can actually store anything you like |
2068
|
|
|
|
|
|
|
in here. :-) |
2069
|
|
|
|
|
|
|
|
2070
|
|
|
|
|
|
|
=cut |
2071
|
|
|
|
|
|
|
|
2072
|
|
|
|
|
|
|
sub prototype_for { |
2073
|
20637
|
|
|
20637
|
1
|
21190
|
my $self = shift; |
2074
|
20637
|
|
|
|
|
20342
|
my $class = shift; |
2075
|
20637
|
100
|
|
|
|
32541
|
if(@_) { |
2076
|
371
|
|
|
|
|
1095
|
return $$$self{pf}{$class} = shift |
2077
|
|
|
|
|
|
|
} |
2078
|
|
|
|
|
|
|
else { |
2079
|
20266
|
|
66
|
|
|
63428
|
return $$$self{pf}{$class} || |
2080
|
|
|
|
|
|
|
($self->prop($class) || return undef)->prop('prototype'); |
2081
|
|
|
|
|
|
|
} |
2082
|
|
|
|
|
|
|
} |
2083
|
|
|
|
|
|
|
|
2084
|
|
|
|
|
|
|
|
2085
|
|
|
|
|
|
|
|
2086
|
|
|
|
|
|
|
=back |
2087
|
|
|
|
|
|
|
|
2088
|
|
|
|
|
|
|
=cut |
2089
|
|
|
|
|
|
|
|
2090
|
|
|
|
|
|
|
|
2091
|
|
|
|
|
|
|
|
2092
|
|
|
|
|
|
|
1; |
2093
|
|
|
|
|
|
|
__END__ |