superchris-rubyjs 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +131 -0
- data/Rakefile +65 -0
- data/bin/rubyjs +144 -0
- data/rubyjs.gemspec +112 -0
- data/src/rubyjs.rb +3 -0
- data/src/rubyjs/code_generator.rb +474 -0
- data/src/rubyjs/compiler.rb +2061 -0
- data/src/rubyjs/debug_name_generator.rb +95 -0
- data/src/rubyjs/encoder.rb +171 -0
- data/src/rubyjs/eval_into.rb +59 -0
- data/src/rubyjs/lib/core.rb +1016 -0
- data/src/rubyjs/lib/dom_element.rb +66 -0
- data/src/rubyjs/lib/json.rb +101 -0
- data/src/rubyjs/lib/microunit.rb +188 -0
- data/src/rubyjs/model.rb +293 -0
- data/src/rubyjs/name_generator.rb +71 -0
- data/src/rwt/AbsolutePanel.rb +161 -0
- data/src/rwt/DOM.Konqueror.rb +89 -0
- data/src/rwt/DOM.Opera.rb +65 -0
- data/src/rwt/DOM.rb +1044 -0
- data/src/rwt/Event.Opera.rb +35 -0
- data/src/rwt/Event.rb +429 -0
- data/src/rwt/HTTPRequest.IE6.rb +5 -0
- data/src/rwt/HTTPRequest.rb +74 -0
- data/src/rwt/Label.rb +164 -0
- data/src/rwt/Panel.rb +90 -0
- data/src/rwt/RootPanel.rb +16 -0
- data/src/rwt/UIObject.rb +495 -0
- data/src/rwt/Widget.rb +193 -0
- data/src/rwt/ported-from/AbsolutePanel.java +158 -0
- data/src/rwt/ported-from/DOM.java +571 -0
- data/src/rwt/ported-from/DOMImpl.java +426 -0
- data/src/rwt/ported-from/DOMImplOpera.java +82 -0
- data/src/rwt/ported-from/DOMImplStandard.java +234 -0
- data/src/rwt/ported-from/HTTPRequest.java +81 -0
- data/src/rwt/ported-from/HTTPRequestImpl.java +103 -0
- data/src/rwt/ported-from/Label.java +163 -0
- data/src/rwt/ported-from/Panel.java +99 -0
- data/src/rwt/ported-from/UIObject.java +614 -0
- data/src/rwt/ported-from/Widget.java +221 -0
- data/test/benchmark/bm_vm1_block.rb +15 -0
- data/test/benchmark/bm_vm1_const.rb +13 -0
- data/test/benchmark/bm_vm1_ensure.rb +15 -0
- data/test/benchmark/common.rb +5 -0
- data/test/benchmark/params.yaml +7 -0
- data/test/common.Browser.rb +13 -0
- data/test/common.rb +8 -0
- data/test/gen_browser_test_suite.rb +129 -0
- data/test/gen_test_suite.rb +41 -0
- data/test/run_benchs.rb +58 -0
- data/test/run_tests.rb +22 -0
- data/test/test_args.rb +24 -0
- data/test/test_array.rb +22 -0
- data/test/test_case.rb +35 -0
- data/test/test_class.rb +55 -0
- data/test/test_eql.rb +9 -0
- data/test/test_exception.rb +61 -0
- data/test/test_expr.rb +12 -0
- data/test/test_hash.rb +29 -0
- data/test/test_hot_ruby.rb +146 -0
- data/test/test_if.rb +28 -0
- data/test/test_insertion_sort.rb +25 -0
- data/test/test_inspect.rb +10 -0
- data/test/test_lebewesen.rb +39 -0
- data/test/test_massign.rb +66 -0
- data/test/test_new.rb +12 -0
- data/test/test_range.rb +70 -0
- data/test/test_regexp.rb +22 -0
- data/test/test_send.rb +65 -0
- data/test/test_simple_output.rb +5 -0
- data/test/test_splat.rb +21 -0
- data/test/test_string.rb +51 -0
- data/test/test_test.rb +17 -0
- data/test/test_yield.rb +154 -0
- data/utils/js/Makefile +9 -0
- data/utils/js/RunScript.class +0 -0
- data/utils/js/RunScript.java +73 -0
- data/utils/js/js.jar +0 -0
- data/utils/js/run.sh +3 -0
- data/utils/jsc/Makefile +7 -0
- data/utils/jsc/README +3 -0
- data/utils/jsc/RunScript.c +93 -0
- data/utils/jsc/run.sh +15 -0
- data/utils/yuicompressor/README +1 -0
- data/utils/yuicompressor/yuicompressor-2.2.5.jar +0 -0
- metadata +157 -0
@@ -0,0 +1,2061 @@
|
|
1
|
+
#
|
2
|
+
# A Ruby to Javascript compiler.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de).
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'sexp_processor'
|
9
|
+
require 'set'
|
10
|
+
|
11
|
+
class MethodCompiler < SexpProcessor
|
12
|
+
|
13
|
+
def initialize(model, method_name)
|
14
|
+
super()
|
15
|
+
|
16
|
+
# don't stop at unknown nodes
|
17
|
+
self.strict = true
|
18
|
+
|
19
|
+
# remove the type from the sexp array
|
20
|
+
self.auto_shift_type = true
|
21
|
+
|
22
|
+
# warn whenever the default method is called, i.e.
|
23
|
+
# an unknown sexp is processed.
|
24
|
+
self.warn_on_default = true
|
25
|
+
|
26
|
+
# expected result class
|
27
|
+
self.expected = String
|
28
|
+
|
29
|
+
#
|
30
|
+
# Used to collect the dynamic variable declarations for
|
31
|
+
# an iterator.
|
32
|
+
#
|
33
|
+
@current_iter_dvars = nil
|
34
|
+
|
35
|
+
#
|
36
|
+
# The model object used for encoding and name generation of all
|
37
|
+
# kind of variables etc.
|
38
|
+
#
|
39
|
+
@model = model
|
40
|
+
|
41
|
+
#
|
42
|
+
# The (encoded) name of the method being compiled
|
43
|
+
#
|
44
|
+
@method_name = method_name
|
45
|
+
|
46
|
+
#
|
47
|
+
# record all local variables (including arguments)
|
48
|
+
#
|
49
|
+
@local_variables = Set.new
|
50
|
+
|
51
|
+
#
|
52
|
+
# Those local variables that need not be initialized with "nil" are
|
53
|
+
# contained in this set (mostly temporary variables).
|
54
|
+
#
|
55
|
+
@local_variables_need_no_initialization = Set.new
|
56
|
+
|
57
|
+
#
|
58
|
+
# contains all argument variables except "*args"
|
59
|
+
#
|
60
|
+
@arguments_no_splat = []
|
61
|
+
|
62
|
+
#
|
63
|
+
# Contains the name of a splat argument "*args"
|
64
|
+
# or nil if none has been specified.
|
65
|
+
#
|
66
|
+
@argument_splat = nil
|
67
|
+
|
68
|
+
#
|
69
|
+
# Include all arguments
|
70
|
+
#
|
71
|
+
@argument_variables = Set.new
|
72
|
+
|
73
|
+
#
|
74
|
+
# If the result should be an expression, this is set to true.
|
75
|
+
#
|
76
|
+
@want_expression = false
|
77
|
+
|
78
|
+
#
|
79
|
+
# Usually each method introduces a new local variable scope.
|
80
|
+
# Make sure that there is no nested :scope.
|
81
|
+
#
|
82
|
+
@scope_nesting = 0
|
83
|
+
|
84
|
+
#
|
85
|
+
# We collect all instance variable reads, because they might not be
|
86
|
+
# initialized yet and we want them to be initialized as "nil"!
|
87
|
+
#
|
88
|
+
@read_instance_variables = Set.new
|
89
|
+
|
90
|
+
#
|
91
|
+
# Use to reuse unused temporary variables
|
92
|
+
#
|
93
|
+
@temporary_variables_pool = []
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
# If a return value should be assigned or not.
|
98
|
+
#
|
99
|
+
@want_result = false
|
100
|
+
|
101
|
+
|
102
|
+
#
|
103
|
+
# contains the name of the block argument, e.g.
|
104
|
+
#
|
105
|
+
# def a(&block)
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# would contain the encoded name of "block".
|
109
|
+
#
|
110
|
+
@block_arg_name = nil
|
111
|
+
|
112
|
+
#
|
113
|
+
# Argument name used for the block argument:
|
114
|
+
#
|
115
|
+
# function (#{@block_name}, ...) { ... }
|
116
|
+
#
|
117
|
+
# Generated on demand with block_name().
|
118
|
+
#
|
119
|
+
@block_name = nil
|
120
|
+
|
121
|
+
#
|
122
|
+
# The name of the variable that contains the return value.
|
123
|
+
#
|
124
|
+
@result_name = nil
|
125
|
+
|
126
|
+
#
|
127
|
+
# For implementing +break+ and +next+ we
|
128
|
+
# have to keep track whether the +break+
|
129
|
+
# or +next+ statement occurs inside a
|
130
|
+
# +while+ loop or inside of a block.
|
131
|
+
#
|
132
|
+
@block_whileloop_stack = []
|
133
|
+
|
134
|
+
#
|
135
|
+
# Whether the method body contains any code that
|
136
|
+
# makes use of iterators (e.g. yield, &block).
|
137
|
+
#
|
138
|
+
@iterators_used = false
|
139
|
+
|
140
|
+
#
|
141
|
+
# Used in a zsuper call (super without arguments)
|
142
|
+
#
|
143
|
+
@arguments_name = nil
|
144
|
+
|
145
|
+
#
|
146
|
+
# Used in catch statements
|
147
|
+
#
|
148
|
+
@exception_name = nil
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
#
|
153
|
+
# Each method has a unique id assigned to it (a simple integer).
|
154
|
+
# This is used for returns that occur inside a code-block, which
|
155
|
+
# have to return the method in which the code-block is declared and
|
156
|
+
# NOT the method in which it is called".
|
157
|
+
#
|
158
|
+
@unique_method_scope = nil
|
159
|
+
end
|
160
|
+
|
161
|
+
def compile_method(pt)
|
162
|
+
process(pt)
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
#
|
167
|
+
#
|
168
|
+
def want_expression(wish=true)
|
169
|
+
old = @want_expression
|
170
|
+
@want_expression = wish
|
171
|
+
res = yield
|
172
|
+
@want_expression = old
|
173
|
+
return res
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# There exist a short cut in the node tree for simple getter and
|
178
|
+
# setter-methods:
|
179
|
+
#
|
180
|
+
# [:defn, :method_name, [:ivar, :variable_name]]
|
181
|
+
#
|
182
|
+
# [:defn, :method_name, [:attrset, :variable_name]]
|
183
|
+
#
|
184
|
+
# We don't want to handle them differently, that's why we expand them
|
185
|
+
# into:
|
186
|
+
#
|
187
|
+
# [:block, [:ivar, :variable_name]]
|
188
|
+
#
|
189
|
+
# [:block, [:args, :v], [:iasgn, :variable_name, [:lvar, :v]]]
|
190
|
+
#
|
191
|
+
def process_defn(exp)
|
192
|
+
raise if @result_name
|
193
|
+
method_name = exp.shift || raise
|
194
|
+
exp = exp.shift || raise
|
195
|
+
|
196
|
+
method_body = want_result do
|
197
|
+
case exp.first
|
198
|
+
when :ivar
|
199
|
+
process(s(:block, exp))
|
200
|
+
when :attrset
|
201
|
+
process(s(:block, s(:args, :_), s(:iasgn, exp[1], s(:lvar, :_))))
|
202
|
+
when :scope, :block, :fbody
|
203
|
+
process(exp)
|
204
|
+
else
|
205
|
+
raise
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
str = ""
|
210
|
+
|
211
|
+
if @argument_variables.empty?
|
212
|
+
str << "function(){"
|
213
|
+
else
|
214
|
+
args_str = ([block_name()] + @arguments_no_splat).join(",")
|
215
|
+
str << "function(#{args_str}){"
|
216
|
+
end
|
217
|
+
|
218
|
+
raise if @local_variables.include?(@model.encode_self)
|
219
|
+
raise if @argument_variables.include?(@model.encode_self)
|
220
|
+
|
221
|
+
#
|
222
|
+
# Add "self" to the local variables
|
223
|
+
#
|
224
|
+
@local_variables.add(@model.encode_self)
|
225
|
+
@local_variables_need_no_initialization.add(@model.encode_self)
|
226
|
+
|
227
|
+
#
|
228
|
+
# declare local variables (except arguments)
|
229
|
+
#
|
230
|
+
to_declare = (@local_variables - @argument_variables).to_a
|
231
|
+
to_declare << @result_name if @result_name
|
232
|
+
unless to_declare.empty?
|
233
|
+
str << "var " + to_declare.join(",") + sep()
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# initialize all local variables (that need initialization) to nil
|
238
|
+
#
|
239
|
+
to_initialize = (@local_variables - @argument_variables - @local_variables_need_no_initialization).to_a
|
240
|
+
to_initialize << @result_name if @result_name
|
241
|
+
unless to_initialize.empty?
|
242
|
+
str << to_initialize.join("=")
|
243
|
+
str << "=#{@model.encode_nil}#{sep()}"
|
244
|
+
end
|
245
|
+
|
246
|
+
#
|
247
|
+
# initialize "self"
|
248
|
+
#
|
249
|
+
str << "#{@model.encode_self}=this#{sep()}"
|
250
|
+
|
251
|
+
#
|
252
|
+
# If a block argument is given (&block) convert it to nil if it is
|
253
|
+
# undefined.
|
254
|
+
#
|
255
|
+
if @block_arg_name
|
256
|
+
str << "#{@block_arg_name}=#{block_name()}==null?#{@model.encode_nil}:#{block_name()}#{sep()}"
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# generate initialization code for each read instance variable
|
261
|
+
#
|
262
|
+
@read_instance_variables.each do |iv|
|
263
|
+
str << "if(#{@model.encode_self}.#{iv}==null)#{@model.encode_self}.#{iv}=#{@model.encode_nil}#{sep()}"
|
264
|
+
end
|
265
|
+
|
266
|
+
#
|
267
|
+
# Used in a zsuper call to refer to the methods "arguments"
|
268
|
+
# "arguments" itself does not work due to iterators using functions.
|
269
|
+
if @arguments_name
|
270
|
+
str << "var #{@arguments_name}=arguments#{sep()}"
|
271
|
+
end
|
272
|
+
|
273
|
+
method_body << "#{sep()}return #{@result_name}" if @result_name
|
274
|
+
|
275
|
+
if @iterators_used
|
276
|
+
str << "try{"
|
277
|
+
str << method_body
|
278
|
+
|
279
|
+
#
|
280
|
+
# Declare variable x?
|
281
|
+
# No, catch introduced a new scope, so we don't have to
|
282
|
+
# use a local or temporary variable here!
|
283
|
+
#
|
284
|
+
x = exception_name()
|
285
|
+
iter_jump = @model.encode_globalattr("iter_jump")
|
286
|
+
return_value = @model.encode_attr("return_value")
|
287
|
+
scope = @model.encode_attr("scope")
|
288
|
+
uid = unique_method_scope()
|
289
|
+
|
290
|
+
str << "}catch(#{x}){"
|
291
|
+
# scope == null or scope == uid
|
292
|
+
str << "if(#{x} instanceof #{iter_jump} && (!#{x}.#{scope} || #{x}.#{scope}==#{uid}))return #{x}.#{return_value}#{sep()}"
|
293
|
+
str << "throw(#{x})}"
|
294
|
+
else
|
295
|
+
str << method_body
|
296
|
+
end
|
297
|
+
|
298
|
+
str << "}"
|
299
|
+
|
300
|
+
return str
|
301
|
+
end
|
302
|
+
|
303
|
+
def sep
|
304
|
+
if $RUBYJS__OPTS.include?('PrettyPrint')
|
305
|
+
";\n"
|
306
|
+
else
|
307
|
+
";"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def unique_method_scope
|
312
|
+
@unique_method_scope ||= @model.next_unique_scope_id
|
313
|
+
end
|
314
|
+
|
315
|
+
def process_scope(exp)
|
316
|
+
raise "nested scope" if @scope_nesting > 0
|
317
|
+
@scope_nesting += 1
|
318
|
+
res = process(exp.shift)
|
319
|
+
@scope_nesting -= 1
|
320
|
+
return res
|
321
|
+
end
|
322
|
+
|
323
|
+
def process_fbody(exp)
|
324
|
+
process(exp.shift)
|
325
|
+
end
|
326
|
+
|
327
|
+
def process_block(exp)
|
328
|
+
raise "empty block" if exp.empty?
|
329
|
+
|
330
|
+
res = []
|
331
|
+
|
332
|
+
# all statements except the last don't want a result
|
333
|
+
without_result do
|
334
|
+
exp[0..-2].each do |stmt|
|
335
|
+
res << process(stmt)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
# last statement
|
339
|
+
res << process(exp[-1])
|
340
|
+
|
341
|
+
res = res.reject {|r| r.nil? || r.empty? || r == "" || r == ";"}
|
342
|
+
|
343
|
+
exp.clear
|
344
|
+
|
345
|
+
if @want_expression
|
346
|
+
"(" + res.join(",") + ")"
|
347
|
+
else
|
348
|
+
res.join(sep())
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
#
|
353
|
+
# STATEMENT
|
354
|
+
#
|
355
|
+
def process_block_arg(exp)
|
356
|
+
raise if @want_expression || @block_arg_name
|
357
|
+
block = exp.shift
|
358
|
+
block_name() # make sure that the function signature contains a block argument!
|
359
|
+
@block_arg_name = @model.encode_local_variable(block)
|
360
|
+
@local_variables.add(@block_arg_name)
|
361
|
+
@local_variables_need_no_initialization.add(@block_arg_name)
|
362
|
+
return ""
|
363
|
+
end
|
364
|
+
|
365
|
+
#
|
366
|
+
# TODO?
|
367
|
+
#
|
368
|
+
def process_block_pass(exp)
|
369
|
+
block = exp.shift
|
370
|
+
call = exp.shift
|
371
|
+
without_result do
|
372
|
+
want_expression do
|
373
|
+
put_iter(process(block))
|
374
|
+
end
|
375
|
+
end
|
376
|
+
process(call)
|
377
|
+
end
|
378
|
+
|
379
|
+
def throw_argument_error(n)
|
380
|
+
@model.add_method_call(m = @model.encode_method("new"))
|
381
|
+
"throw(" + @model.lookup_constant('::ArgumentError') +
|
382
|
+
".#{m}(#{@model.encode_nil},'wrong number of arguments ('+Math.max(0,arguments.length-1).toString()+' for #{n})'))"
|
383
|
+
end
|
384
|
+
|
385
|
+
#
|
386
|
+
# STATEMENT
|
387
|
+
#
|
388
|
+
# Method arguments.
|
389
|
+
#
|
390
|
+
# Generate arity checks and default argument assignment.
|
391
|
+
#
|
392
|
+
def process_args(exp)
|
393
|
+
raise if @want_expression
|
394
|
+
|
395
|
+
args = []
|
396
|
+
default_values = nil
|
397
|
+
|
398
|
+
loop do
|
399
|
+
arg = exp.shift
|
400
|
+
break if arg.nil?
|
401
|
+
if arg.is_a?(Symbol)
|
402
|
+
args << arg
|
403
|
+
else
|
404
|
+
raise unless exp.empty?
|
405
|
+
default_values = arg
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
args.each do |arg|
|
410
|
+
arg = arg.to_s
|
411
|
+
if arg[0,1] == '*'
|
412
|
+
raise if @argument_splat
|
413
|
+
@argument_splat = @model.encode_local_variable(arg[1..-1])
|
414
|
+
# argument_splat is not an argument in the function's argument list
|
415
|
+
@local_variables.add(@argument_splat)
|
416
|
+
else
|
417
|
+
v = @model.encode_local_variable(arg)
|
418
|
+
@arguments_no_splat << v
|
419
|
+
@local_variables.add(v)
|
420
|
+
@argument_variables.add(v)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# that's not the correct arity, but we decrease it by one for each
|
425
|
+
# optional argument.
|
426
|
+
min_arity = @arguments_no_splat.size
|
427
|
+
max_arity = @arguments_no_splat.size
|
428
|
+
|
429
|
+
str = ""
|
430
|
+
|
431
|
+
#
|
432
|
+
# Generate code for the default values of arguments. We check
|
433
|
+
# whether a argument has been assigned a value, if not (== null or == undefined),
|
434
|
+
# then we assign the default value.
|
435
|
+
#
|
436
|
+
# NOTE: A check to ==null also returns true if the argument is undefined.
|
437
|
+
#
|
438
|
+
if default_values
|
439
|
+
raise unless default_values[0] == :block
|
440
|
+
default_values[1..-1].each do |dv|
|
441
|
+
min_arity -= 1
|
442
|
+
raise unless dv[0] == :lasgn
|
443
|
+
raise unless dv.size == 3
|
444
|
+
arg = @model.encode_local_variable(dv[1])
|
445
|
+
@local_variables.add(arg)
|
446
|
+
@argument_variables.add(arg)
|
447
|
+
value = dv[2]
|
448
|
+
|
449
|
+
str << "if(#{arg}==null)"
|
450
|
+
str << "#{arg}="
|
451
|
+
str << want_expression do process(value) end
|
452
|
+
str << sep()
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
# now as we know the min_arity, we prepend an arity check before the
|
457
|
+
# code generated above.
|
458
|
+
str2 = ""
|
459
|
+
|
460
|
+
if @argument_splat
|
461
|
+
# max_arity == infinity => no check
|
462
|
+
|
463
|
+
if min_arity == 0
|
464
|
+
# min_arity == infinity as well => we need no check
|
465
|
+
else
|
466
|
+
# +1 because we have a block argument anyway.
|
467
|
+
str2 << "if(arguments.length<#{min_arity+1})#{throw_argument_error(min_arity)}#{sep()}"
|
468
|
+
end
|
469
|
+
else
|
470
|
+
if min_arity == 0
|
471
|
+
# can't be less than 0 arguments anyway! => no check
|
472
|
+
str2 << "if(arguments.length>#{max_arity+1})#{throw_argument_error(max_arity)}#{sep()}"
|
473
|
+
else
|
474
|
+
if min_arity == max_arity
|
475
|
+
str2 << "if(arguments.length!=#{min_arity+1})#{throw_argument_error(min_arity)}#{sep()}"
|
476
|
+
else
|
477
|
+
str2 << "if(arguments.length<#{min_arity+1})#{throw_argument_error(min_arity)}#{sep()}"
|
478
|
+
str2 << "if(arguments.length>#{max_arity+1})#{throw_argument_error(max_arity)}#{sep()}"
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
# NoArgumentArityChecks disable argument arity checks
|
484
|
+
if $RUBYJS__OPTS.include?('NoArgumentArityChecks')
|
485
|
+
else
|
486
|
+
# prepend
|
487
|
+
str = str2 + str
|
488
|
+
end
|
489
|
+
|
490
|
+
if @argument_splat
|
491
|
+
# construct the code to initialize the splat argument.
|
492
|
+
# unluckily the arguments object is not an array, instead it's a
|
493
|
+
# special object that has only the length() and [] methods. There
|
494
|
+
# is no way to convert it to an array, except looping over each
|
495
|
+
# value and pushing the value into a new array.
|
496
|
+
# FIXME: variable "i"
|
497
|
+
str << "#{@argument_splat}=[]#{sep()}"
|
498
|
+
@local_variables_need_no_initialization.add(@argument_splat)
|
499
|
+
with_temporary_variable do |i|
|
500
|
+
@local_variables_need_no_initialization.add(i)
|
501
|
+
str << "for(#{i}=#{@arguments_no_splat.size+1};#{i}<arguments.length;#{i}++)#{@argument_splat}.push(arguments[#{i}])#{sep()}"
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
return str
|
506
|
+
end
|
507
|
+
|
508
|
+
|
509
|
+
#
|
510
|
+
# Same as generate_method_call, but where recv_exp is an sexp.
|
511
|
+
# We take care that it gets processed, and that in case of a
|
512
|
+
# numeric literal, it gets surrounded by parens.
|
513
|
+
#
|
514
|
+
def generate_method_call_receiver_exp(recv_exp, method, iter, args)
|
515
|
+
generate_method_call(receiver_exp(recv_exp), method, iter, args)
|
516
|
+
end
|
517
|
+
|
518
|
+
def receiver_exp(recv_exp)
|
519
|
+
is_num = is_numeric_literal(recv_exp)
|
520
|
+
receiver = want_expression do process(recv_exp) end
|
521
|
+
receiver = "(" + receiver + ")" if is_num
|
522
|
+
return receiver
|
523
|
+
end
|
524
|
+
|
525
|
+
#
|
526
|
+
# Generates a method call.
|
527
|
+
#
|
528
|
+
def generate_method_call(receiver, method, iter, args)
|
529
|
+
|
530
|
+
method_name = @model.encode_method(method)
|
531
|
+
@model.add_method_call(method_name)
|
532
|
+
|
533
|
+
without_result do
|
534
|
+
want_expression do
|
535
|
+
if args.nil?
|
536
|
+
# no arguments
|
537
|
+
#
|
538
|
+
# NOTE: We don't have to encode an iter of "nil" as "nil".
|
539
|
+
# Instead we save us the space and check for undefined in the
|
540
|
+
# method definition.
|
541
|
+
"#{receiver}.#{method_name}(#{iter})"
|
542
|
+
elsif args.first == :array
|
543
|
+
# one or more arguments
|
544
|
+
args_string = args[1..-1].map{|a| process(a)}.join(",")
|
545
|
+
"#{receiver}.#{method_name}(#{iter || @model.encode_nil},#{args_string})"
|
546
|
+
elsif args.first == :splat or args.first == :argscat
|
547
|
+
#
|
548
|
+
# puts(*a) # => [:fcall, :puts, [:splat, [:lvar, :a]]]]]]
|
549
|
+
#
|
550
|
+
# puts(1, *a) # => ... [:argscat, [:array, [:lit, 1]], [:lvar, :a]]
|
551
|
+
#
|
552
|
+
@model.add_method_call(__invoke = @model.encode_method('__invoke'))
|
553
|
+
"#{receiver}.#{__invoke}(#{iter || @model.encode_nil},'#{method_name}',#{ process(args) })"
|
554
|
+
else
|
555
|
+
raise
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
#
|
562
|
+
# Generates a super call.
|
563
|
+
#
|
564
|
+
def generate_super_call(iter, args)
|
565
|
+
@model.add_method_call(@method_name)
|
566
|
+
|
567
|
+
args_str = without_result do
|
568
|
+
want_expression do
|
569
|
+
if args.nil?
|
570
|
+
# no arguments
|
571
|
+
#
|
572
|
+
# NOTE: We don't have to encode an iter of "nil" as "nil".
|
573
|
+
# Instead we save us the space and check for undefined in the
|
574
|
+
# method definition.
|
575
|
+
"[]"
|
576
|
+
else
|
577
|
+
process(args)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
sc = @model.encode_globalattr('supercall')
|
583
|
+
"#{sc}(#{@model.encode_self},'#{@method_name}',#{iter || @model.encode_nil},#{args_str})"
|
584
|
+
end
|
585
|
+
|
586
|
+
#
|
587
|
+
# EXPRESSION
|
588
|
+
#
|
589
|
+
# Super call.
|
590
|
+
#
|
591
|
+
def process_super(exp)
|
592
|
+
args = exp.shift
|
593
|
+
generate_super_call(get_iter(), args)
|
594
|
+
end
|
595
|
+
|
596
|
+
#
|
597
|
+
# EXPRESSION
|
598
|
+
#
|
599
|
+
# Super call without arguments "super"
|
600
|
+
#
|
601
|
+
# We need to introduce a new variable instead of
|
602
|
+
# using "arguments" because "arguments" is not
|
603
|
+
# available when called within an iterator, because
|
604
|
+
# an iterator introduces a new function.
|
605
|
+
#
|
606
|
+
def process_zsuper(exp)
|
607
|
+
@model.add_method_call(@method_name)
|
608
|
+
sc = @model.encode_globalattr('zsupercall')
|
609
|
+
"#{sc}(#{@model.encode_self},'#{@method_name}',#{arguments_name()})"
|
610
|
+
end
|
611
|
+
|
612
|
+
#
|
613
|
+
# EXPRESSION
|
614
|
+
#
|
615
|
+
# Method call without receiver
|
616
|
+
#
|
617
|
+
def process_fcall(exp)
|
618
|
+
method = exp.shift
|
619
|
+
args = exp.shift
|
620
|
+
|
621
|
+
str = without_result do
|
622
|
+
generate_method_call(@model.encode_self, method, get_iter(), args)
|
623
|
+
end
|
624
|
+
|
625
|
+
resultify(str)
|
626
|
+
end
|
627
|
+
|
628
|
+
#
|
629
|
+
# for var in expr do ... end
|
630
|
+
#
|
631
|
+
def process_for(exp)
|
632
|
+
receiver = exp.shift
|
633
|
+
asgn = exp.shift
|
634
|
+
block = exp.shift
|
635
|
+
|
636
|
+
new_exp = [:iter,
|
637
|
+
[:call, receiver, :each],
|
638
|
+
asgn,
|
639
|
+
block]
|
640
|
+
|
641
|
+
process(new_exp)
|
642
|
+
end
|
643
|
+
|
644
|
+
def process_dot2(exp)
|
645
|
+
range(exp, false)
|
646
|
+
end
|
647
|
+
|
648
|
+
def process_dot3(exp)
|
649
|
+
range(exp, true)
|
650
|
+
end
|
651
|
+
|
652
|
+
def range(exp, exclude_end)
|
653
|
+
from = exp.shift
|
654
|
+
to = exp.shift
|
655
|
+
|
656
|
+
without_result do
|
657
|
+
want_expression do
|
658
|
+
from = process(from)
|
659
|
+
to = process(to)
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
range = @model.lookup_constant('::Range')
|
664
|
+
@model.add_method_call(m = @model.encode_method("new"))
|
665
|
+
res = "#{range}.#{m}(#{@model.encode_nil},#{from},#{to},#{exclude_end})"
|
666
|
+
resultify(res)
|
667
|
+
end
|
668
|
+
|
669
|
+
#
|
670
|
+
# EXPRESSION
|
671
|
+
#
|
672
|
+
# Attribute assignment: receiver.attr=(value)
|
673
|
+
#
|
674
|
+
# Same as a method call!
|
675
|
+
#
|
676
|
+
def process_attrasgn(exp)
|
677
|
+
process_call(exp)
|
678
|
+
end
|
679
|
+
|
680
|
+
#
|
681
|
+
# The ||= operator.
|
682
|
+
#
|
683
|
+
# For example:
|
684
|
+
#
|
685
|
+
# a = a || "hallo"
|
686
|
+
#
|
687
|
+
# [:lasgn, :a, [:or, [:lvar, :a], [:str, "hallo"]]],
|
688
|
+
#
|
689
|
+
# a ||= "hallo"
|
690
|
+
#
|
691
|
+
# [:op_asgn_or, [:lvar, :a], [:lasgn, :a, [:str, "hallo"]]]]]]
|
692
|
+
#
|
693
|
+
# We rewrite the one to the other.
|
694
|
+
#
|
695
|
+
def process_op_asgn_or(exp)
|
696
|
+
ref = exp.shift
|
697
|
+
asgn = exp.shift
|
698
|
+
asgn[2] = [:or, ref, asgn[2]]
|
699
|
+
process(asgn)
|
700
|
+
end
|
701
|
+
|
702
|
+
#
|
703
|
+
# EXPRESSION
|
704
|
+
#
|
705
|
+
# Method call with receiver
|
706
|
+
#
|
707
|
+
def process_call(exp)
|
708
|
+
receiver = exp.shift
|
709
|
+
method = exp.shift
|
710
|
+
args = exp.shift
|
711
|
+
|
712
|
+
iter = get_iter()
|
713
|
+
|
714
|
+
#
|
715
|
+
# RubyJS::inline("str")
|
716
|
+
#
|
717
|
+
# A different form of Javascript inline. We have this form
|
718
|
+
# because ``-style Javascript-inline has a different esacping
|
719
|
+
# of \. Sometimes we want no escaping, so
|
720
|
+
#
|
721
|
+
# RubyJS::inline %q(...) could be used.
|
722
|
+
#
|
723
|
+
if receiver == [:const, :RubyJS] and method == :inline and
|
724
|
+
args[0] == :array and args.size == 2 and args[1].size == 2 and args[1][0] == :str and iter.nil?
|
725
|
+
raise if @want_expression
|
726
|
+
return @model.interpolate(args[1][1])
|
727
|
+
end
|
728
|
+
|
729
|
+
str =
|
730
|
+
if $RUBYJS__OPTS.include?('OptimizeArithOps') and %w(> >= < <= + - * /).include?(method.to_s) and
|
731
|
+
iter.nil? and args and args[0] == :array and args.size == 2
|
732
|
+
without_result do
|
733
|
+
want_expression do
|
734
|
+
"(#{process(receiver)})#{method.to_s}(#{process(args[1])})"
|
735
|
+
end
|
736
|
+
end
|
737
|
+
else
|
738
|
+
without_result do
|
739
|
+
generate_method_call_receiver_exp(receiver, method, iter, args)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
resultify(str)
|
743
|
+
end
|
744
|
+
|
745
|
+
#
|
746
|
+
# EXPRESSION
|
747
|
+
#
|
748
|
+
# Virtual call. Either a method call or a variable, determined at
|
749
|
+
# runtime.
|
750
|
+
#
|
751
|
+
# Ruby cannot distinguish a local variable and a method called without
|
752
|
+
# parentheses and without receiver at parse time. Example:
|
753
|
+
#
|
754
|
+
# def my_method
|
755
|
+
# method_or_not.inspect
|
756
|
+
# end
|
757
|
+
#
|
758
|
+
# If a local variable of that name exists, then it is used as
|
759
|
+
# receiver. Otherwise it is seen as a method and is called.
|
760
|
+
#
|
761
|
+
# NOTE: A vcall can never have an iterator! Because if an iterator is
|
762
|
+
# specified it is automatically no variable and as such a fcall or
|
763
|
+
# call is generated.
|
764
|
+
#
|
765
|
+
# NOTE: As RubyJS disallows to use eval(), a vcall can never be a
|
766
|
+
# local variable (FIXME: to be prooved).
|
767
|
+
#
|
768
|
+
def process_vcall(exp)
|
769
|
+
method = exp.shift
|
770
|
+
|
771
|
+
resultify(generate_method_call(@model.encode_self, method, nil, nil))
|
772
|
+
end
|
773
|
+
|
774
|
+
#
|
775
|
+
# EXPRESSION
|
776
|
+
#
|
777
|
+
# Q: Always call with "nil" if no argument is given?
|
778
|
+
# A: No, because then we could not distinguish between
|
779
|
+
# no arguments and "nil" as argument.
|
780
|
+
#
|
781
|
+
def process_yield(exp)
|
782
|
+
value = exp.shift
|
783
|
+
|
784
|
+
str = without_result do
|
785
|
+
want_expression do
|
786
|
+
block_name() + "(" + (value ? process(value) : '') + ")"
|
787
|
+
end
|
788
|
+
end
|
789
|
+
resultify(str)
|
790
|
+
end
|
791
|
+
|
792
|
+
#
|
793
|
+
# Constant lookup
|
794
|
+
# ===============
|
795
|
+
#
|
796
|
+
# Constant lookup in RubyJS is performed statically. This is possible
|
797
|
+
# because the whole class hierarchy is available at compile-time.
|
798
|
+
|
799
|
+
#
|
800
|
+
# EXPRESSION
|
801
|
+
#
|
802
|
+
def process_const(exp)
|
803
|
+
resultify(@model.lookup_constant(constify(exp, :const)))
|
804
|
+
end
|
805
|
+
|
806
|
+
#
|
807
|
+
# helper methods
|
808
|
+
#
|
809
|
+
|
810
|
+
def constify(exp, type)
|
811
|
+
exp.unshift(type)
|
812
|
+
const_rec(exp).join("::")
|
813
|
+
end
|
814
|
+
|
815
|
+
def const_rec(exp)
|
816
|
+
case exp.shift
|
817
|
+
when :const
|
818
|
+
return [exp.shift]
|
819
|
+
when :colon2
|
820
|
+
const_rec(exp.shift) + [exp.shift]
|
821
|
+
when :colon3
|
822
|
+
[nil, exp.shift]
|
823
|
+
else
|
824
|
+
raise
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
#
|
829
|
+
# EXPRESSION
|
830
|
+
#
|
831
|
+
# A::B # => [:colon2, [:const, :A], :B]
|
832
|
+
#
|
833
|
+
def process_colon2(exp)
|
834
|
+
resultify(@model.lookup_constant(constify(exp, :colon2)))
|
835
|
+
end
|
836
|
+
|
837
|
+
#
|
838
|
+
# EXPRESSION
|
839
|
+
#
|
840
|
+
# ::A # => [:colon3, :A]
|
841
|
+
#
|
842
|
+
def process_colon3(exp)
|
843
|
+
resultify(@model.lookup_constant(constify(exp, :colon3)))
|
844
|
+
end
|
845
|
+
|
846
|
+
#
|
847
|
+
# EXPRESSION
|
848
|
+
#
|
849
|
+
# Implements the "&&" operator, which has short circuit behaviour.
|
850
|
+
#
|
851
|
+
# a && b
|
852
|
+
#
|
853
|
+
# is equivalent in Ruby to
|
854
|
+
#
|
855
|
+
# if a then b else a end
|
856
|
+
#
|
857
|
+
def process_and(exp)
|
858
|
+
a = exp.shift
|
859
|
+
b = exp.shift
|
860
|
+
|
861
|
+
res = without_result do
|
862
|
+
want_expression do
|
863
|
+
with_temporary_variable do |tmp|
|
864
|
+
@local_variables_need_no_initialization.add(tmp)
|
865
|
+
"(#{tmp}=#{process(a)}, (#{tmp}!==false&&#{tmp}!==nil) ? (#{process(b)}) : #{tmp})"
|
866
|
+
end
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
return resultify(res)
|
871
|
+
end
|
872
|
+
|
873
|
+
#
|
874
|
+
# EXPRESSION
|
875
|
+
#
|
876
|
+
# Implements the "||" operator, which has short circuit behaviour.
|
877
|
+
#
|
878
|
+
# a || b
|
879
|
+
#
|
880
|
+
# is equivalent in Ruby to
|
881
|
+
#
|
882
|
+
# if a then a else b end
|
883
|
+
#
|
884
|
+
def process_or(exp)
|
885
|
+
a = exp.shift
|
886
|
+
b = exp.shift
|
887
|
+
|
888
|
+
res = without_result do
|
889
|
+
want_expression do
|
890
|
+
with_temporary_variable do |tmp|
|
891
|
+
@local_variables_need_no_initialization.add(tmp)
|
892
|
+
"(#{tmp}=#{process(a)}, (#{tmp}!==false&&#{tmp}!==nil) ? #{tmp} : (#{process(b)}))"
|
893
|
+
end
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
return resultify(res)
|
898
|
+
end
|
899
|
+
|
900
|
+
#
|
901
|
+
# EXPRESSION
|
902
|
+
#
|
903
|
+
def process_not(exp)
|
904
|
+
a = exp.shift
|
905
|
+
|
906
|
+
without_result do
|
907
|
+
want_expression do
|
908
|
+
a = conditionalize(a, true)
|
909
|
+
end
|
910
|
+
end
|
911
|
+
|
912
|
+
return resultify(a)
|
913
|
+
end
|
914
|
+
|
915
|
+
#
|
916
|
+
# STATEMENT/EXPRESSION
|
917
|
+
#
|
918
|
+
# unless is converted by the Ruby parser into an "if":
|
919
|
+
#
|
920
|
+
# unless X
|
921
|
+
# block
|
922
|
+
# end
|
923
|
+
#
|
924
|
+
# =>
|
925
|
+
#
|
926
|
+
# if X
|
927
|
+
# else
|
928
|
+
# block
|
929
|
+
# end
|
930
|
+
#
|
931
|
+
def process_if(exp)
|
932
|
+
cond = exp.shift
|
933
|
+
_then = exp.shift
|
934
|
+
_else = exp.shift
|
935
|
+
|
936
|
+
_then_processed = if _then
|
937
|
+
process(_then)
|
938
|
+
else
|
939
|
+
nil
|
940
|
+
end
|
941
|
+
|
942
|
+
_else_processed = if _else
|
943
|
+
process(_else)
|
944
|
+
else
|
945
|
+
nil
|
946
|
+
end
|
947
|
+
|
948
|
+
negate = false
|
949
|
+
|
950
|
+
if _then_processed.nil?
|
951
|
+
# no "then" block as in
|
952
|
+
#
|
953
|
+
# if X
|
954
|
+
# else
|
955
|
+
# block
|
956
|
+
# end
|
957
|
+
#
|
958
|
+
# => convert into
|
959
|
+
#
|
960
|
+
# if not X
|
961
|
+
# block
|
962
|
+
# end
|
963
|
+
#
|
964
|
+
# by swapping then and else blocks
|
965
|
+
# and negating the condition
|
966
|
+
|
967
|
+
_then_processed = _else_processed
|
968
|
+
_else_processed = nil
|
969
|
+
negate = true
|
970
|
+
end
|
971
|
+
|
972
|
+
cond_processed = without_result do
|
973
|
+
conditionalize(cond, negate)
|
974
|
+
end
|
975
|
+
|
976
|
+
str = ""
|
977
|
+
|
978
|
+
if @want_expression
|
979
|
+
_then_processed ||= resultify(@model.encode_nil)
|
980
|
+
_else_processes ||= resultify(@model.encode_nil)
|
981
|
+
str << "(#{cond_processed}?#{_then_processed}:#{_else_processed})"
|
982
|
+
else
|
983
|
+
str << "if(#{cond_processed}){"
|
984
|
+
str << (_then_processed || (@want_result ? resultify(@model.encode_nil) : ''))
|
985
|
+
str << "}"
|
986
|
+
if @want_result
|
987
|
+
_else_processed ||= resultify(@model.encode_nil)
|
988
|
+
end
|
989
|
+
if _else_processed
|
990
|
+
str << "else{"
|
991
|
+
str << _else_processed
|
992
|
+
str << "}"
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
return str
|
997
|
+
end
|
998
|
+
|
999
|
+
#
|
1000
|
+
# STATEMENT/EXPRESSION
|
1001
|
+
#
|
1002
|
+
# case obj
|
1003
|
+
# when c1
|
1004
|
+
# block1
|
1005
|
+
# when c2, c3
|
1006
|
+
# block23
|
1007
|
+
# else
|
1008
|
+
# blockelse
|
1009
|
+
# end
|
1010
|
+
#
|
1011
|
+
# [:case,
|
1012
|
+
# obj,
|
1013
|
+
# [:when, [:array, c1], block1],
|
1014
|
+
# [:when, [:array, c2, c3], block23],
|
1015
|
+
# blockelse or nil
|
1016
|
+
# ]
|
1017
|
+
#
|
1018
|
+
# We are transforming the "case" into "if".
|
1019
|
+
#
|
1020
|
+
# _tmp = obj;
|
1021
|
+
# if c1 === _tmp
|
1022
|
+
# block1
|
1023
|
+
# else
|
1024
|
+
# if c2 === _tmp or c3 === _tmp then
|
1025
|
+
# block23
|
1026
|
+
# else
|
1027
|
+
# blockelse
|
1028
|
+
# end
|
1029
|
+
# end
|
1030
|
+
#
|
1031
|
+
def process_case(exp)
|
1032
|
+
obj = exp.shift
|
1033
|
+
|
1034
|
+
with_temporary_variable do |tmp|
|
1035
|
+
@local_variables_need_no_initialization.add(tmp)
|
1036
|
+
asgn = want_expression do
|
1037
|
+
"#{tmp}=#{process(obj)}"
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
new_exp = current_exp = []
|
1041
|
+
|
1042
|
+
while not exp.empty?
|
1043
|
+
_when = exp.shift
|
1044
|
+
if _when.nil? or _when.first != :when
|
1045
|
+
# last element
|
1046
|
+
raise exp.inspect unless exp.empty?
|
1047
|
+
current_exp << _when
|
1048
|
+
else
|
1049
|
+
conds = _when[1]
|
1050
|
+
block = _when[2]
|
1051
|
+
raise unless conds.first == :array
|
1052
|
+
|
1053
|
+
cond = multi_or(conds[1..-1].map {|c| [:call, c, :===, [:array, [:special_inline_js_value, tmp]]] })
|
1054
|
+
|
1055
|
+
my_exp = [:if, cond, block]
|
1056
|
+
|
1057
|
+
current_exp << my_exp
|
1058
|
+
current_exp = my_exp
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
if_code = process(new_exp.first)
|
1063
|
+
|
1064
|
+
code =
|
1065
|
+
if @want_expression
|
1066
|
+
"(#{asgn}, #{if_code})"
|
1067
|
+
else
|
1068
|
+
"#{asgn}#{sep()}#{if_code}"
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
code
|
1072
|
+
end
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
def multi_or(exprs)
|
1076
|
+
case exprs.size
|
1077
|
+
when 0
|
1078
|
+
raise
|
1079
|
+
when 1
|
1080
|
+
# multi_or([1]) => 1
|
1081
|
+
exprs[0]
|
1082
|
+
when 2
|
1083
|
+
# multi_or([1, 2]) => [:or, 1, 2]
|
1084
|
+
[:or, exprs[0], exprs[1]]
|
1085
|
+
else
|
1086
|
+
# multi_or([1, 2, 3]) => [:or, 1, multi_or([2,3])] == [:or, 1, [:or, 2, 3]]
|
1087
|
+
[:or, exprs[0], multi_or(exprs[1..-1])]
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
#
|
1092
|
+
# STATEMENT
|
1093
|
+
#
|
1094
|
+
def process_begin(exp)
|
1095
|
+
raise if @want_expression
|
1096
|
+
block = exp.shift
|
1097
|
+
process(block)
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
#
|
1101
|
+
# STATEMENT
|
1102
|
+
#
|
1103
|
+
def process_rescue(exp)
|
1104
|
+
raise if @want_expression
|
1105
|
+
block = exp.shift
|
1106
|
+
handler = exp.shift
|
1107
|
+
else_handler = exp.shift
|
1108
|
+
raise unless handler.first == :resbody
|
1109
|
+
|
1110
|
+
x = exception_name()
|
1111
|
+
|
1112
|
+
code = [:block, block]
|
1113
|
+
code << else_handler if else_handler
|
1114
|
+
|
1115
|
+
str = ""
|
1116
|
+
str << "try{#{process(code)}}"
|
1117
|
+
str << "catch(#{x}){"
|
1118
|
+
|
1119
|
+
# check whether it's an iterator break. if yes, pass it on
|
1120
|
+
iter_jump = @model.encode_globalattr("iter_jump")
|
1121
|
+
str << "if(#{x} instanceof #{iter_jump})throw(#{x})#{sep()}"
|
1122
|
+
str << "#{process(handler)}"
|
1123
|
+
str << "}"
|
1124
|
+
|
1125
|
+
str
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
#
|
1129
|
+
# STATEMENT
|
1130
|
+
#
|
1131
|
+
def process_resbody(exp)
|
1132
|
+
raise if @want_expression
|
1133
|
+
condition = exp.shift
|
1134
|
+
if_body = exp.shift
|
1135
|
+
else_body = exp.shift
|
1136
|
+
|
1137
|
+
x = exception_name()
|
1138
|
+
|
1139
|
+
# if only "rescue" is used (and not e.g. "rescue Exception")
|
1140
|
+
condition ||= [:array, [:const, :StandardError]]
|
1141
|
+
raise unless condition.first == :array
|
1142
|
+
|
1143
|
+
# if this is the last resbody (else_body is nil) then
|
1144
|
+
# the exception couldn't be catched and we rethrow it!
|
1145
|
+
else_body ||= [:special_inline_js_value, "throw(#{x})"]
|
1146
|
+
|
1147
|
+
# build condition expression
|
1148
|
+
cond =
|
1149
|
+
multi_or(condition[1..-1].map do |c|
|
1150
|
+
[:call, c, :===, [:array, [:special_inline_js_value, x]]]
|
1151
|
+
end)
|
1152
|
+
|
1153
|
+
process([:if, cond, if_body, else_body])
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
#
|
1157
|
+
# STATEMENT
|
1158
|
+
#
|
1159
|
+
def process_ensure(exp)
|
1160
|
+
raise if @want_expression
|
1161
|
+
try_body = process(exp.shift)
|
1162
|
+
ensure_body = without_result { process(exp.shift) }
|
1163
|
+
|
1164
|
+
"try{#{try_body}}finally{#{ensure_body}}"
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
#
|
1168
|
+
# STATEMENT
|
1169
|
+
#
|
1170
|
+
def process_return(exp)
|
1171
|
+
return_value =
|
1172
|
+
if param = exp.shift
|
1173
|
+
want_expression do
|
1174
|
+
without_result do
|
1175
|
+
process(param)
|
1176
|
+
end
|
1177
|
+
end
|
1178
|
+
else
|
1179
|
+
@model.encode_nil
|
1180
|
+
end
|
1181
|
+
if @block_whileloop_stack.last == :iter
|
1182
|
+
# return from within an iterator
|
1183
|
+
throw_iter_jump(return_value, unique_method_scope())
|
1184
|
+
elsif @want_expression
|
1185
|
+
throw_iter_jump(return_value)
|
1186
|
+
else
|
1187
|
+
"return #{return_value}"
|
1188
|
+
end
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
#
|
1192
|
+
# STATEMENT
|
1193
|
+
#
|
1194
|
+
def process_while(exp)
|
1195
|
+
raise if @want_expression
|
1196
|
+
|
1197
|
+
cond = exp.shift
|
1198
|
+
block = exp.shift
|
1199
|
+
flag = exp.shift
|
1200
|
+
raise unless flag == true # FIXME: document
|
1201
|
+
|
1202
|
+
str = without_result do
|
1203
|
+
c = conditionalize(cond)
|
1204
|
+
@block_whileloop_stack.push(:while)
|
1205
|
+
b = process(block)
|
1206
|
+
@block_whileloop_stack.pop || raise
|
1207
|
+
|
1208
|
+
"while(#{c}){#{b}}"
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
if @want_result
|
1212
|
+
str << sep() + resultify(@model.encode_nil) + sep()
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
return str
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
#
|
1219
|
+
# STATEMENT
|
1220
|
+
#
|
1221
|
+
def process_break(exp)
|
1222
|
+
case @block_whileloop_stack.last
|
1223
|
+
when :while
|
1224
|
+
raise "break with arguments inside while not yet supported" unless exp.empty?
|
1225
|
+
raise if @want_expression
|
1226
|
+
"break"
|
1227
|
+
when :iter
|
1228
|
+
return_value =
|
1229
|
+
if param = exp.shift
|
1230
|
+
without_result do
|
1231
|
+
process(param)
|
1232
|
+
end
|
1233
|
+
else
|
1234
|
+
@model.encode_nil
|
1235
|
+
end
|
1236
|
+
throw_iter_jump(return_value)
|
1237
|
+
when nil
|
1238
|
+
raise("break not in loop/block scope")
|
1239
|
+
else
|
1240
|
+
raise "FATAL"
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
def throw_iter_jump(return_value, scope=nil)
|
1245
|
+
@iterators_used = true # used to produce catch code in this method
|
1246
|
+
iter_jump = @model.encode_globalattr('iter_jump')
|
1247
|
+
scope ||= "null"
|
1248
|
+
th =
|
1249
|
+
if @want_expression
|
1250
|
+
@model.encode_globalattr("throw_expr")
|
1251
|
+
else
|
1252
|
+
"throw"
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
"#{th}(new #{iter_jump}(#{return_value},#{scope}))"
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
#
|
1259
|
+
# STATEMENT
|
1260
|
+
#
|
1261
|
+
def process_next(exp)
|
1262
|
+
case @block_whileloop_stack.last
|
1263
|
+
when :while
|
1264
|
+
raise "next with arguments inside while not yet supported" unless exp.empty?
|
1265
|
+
raise if @want_expression
|
1266
|
+
"continue"
|
1267
|
+
when :iter
|
1268
|
+
# next inside a code-block generates a "return"
|
1269
|
+
raise if @want_expression
|
1270
|
+
|
1271
|
+
return_value =
|
1272
|
+
if param = exp.shift
|
1273
|
+
want_expression do
|
1274
|
+
without_result do
|
1275
|
+
process(param)
|
1276
|
+
end
|
1277
|
+
end
|
1278
|
+
else
|
1279
|
+
@model.encode_nil
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
"return #{return_value}"
|
1283
|
+
when nil
|
1284
|
+
raise("next not in loop/block scope")
|
1285
|
+
else
|
1286
|
+
raise "FATAL"
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
#
|
1291
|
+
# UNDEFINED
|
1292
|
+
#
|
1293
|
+
# Backtick strings: `inline javascript`
|
1294
|
+
#
|
1295
|
+
# We use them for inline Javascript.
|
1296
|
+
#
|
1297
|
+
# It's unclear whether it's a STATEMENT or EXPRESSION.
|
1298
|
+
# It depends on the Javascript.
|
1299
|
+
#
|
1300
|
+
# NOTE: You have to take care to return a value yourself
|
1301
|
+
# in case of @want_result, i.e. there is no automatic handling
|
1302
|
+
# thereof.
|
1303
|
+
#
|
1304
|
+
def process_xstr(exp)
|
1305
|
+
str = exp.shift
|
1306
|
+
@model.interpolate(str)
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
#
|
1310
|
+
# EXPRESSION
|
1311
|
+
#
|
1312
|
+
# String interpolation
|
1313
|
+
#
|
1314
|
+
def process_dstr(exp)
|
1315
|
+
arr = []
|
1316
|
+
arr << exp.shift.inspect
|
1317
|
+
|
1318
|
+
without_result do
|
1319
|
+
want_expression do
|
1320
|
+
exp.each {|e| arr << "(" + process(e) + ")" }
|
1321
|
+
end
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
exp.clear
|
1325
|
+
resultify("(" + arr.join(" + ") + ")")
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
def process_evstr(exp)
|
1329
|
+
e = exp.shift
|
1330
|
+
|
1331
|
+
@model.add_method_call(to_s = @model.encode_method("to_s"))
|
1332
|
+
|
1333
|
+
res = without_result do
|
1334
|
+
want_expression do
|
1335
|
+
"(" + process(e) + ").#{to_s}()"
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
resultify(res)
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
#
|
1343
|
+
# EXPRESSION
|
1344
|
+
#
|
1345
|
+
# Process literals
|
1346
|
+
#
|
1347
|
+
def process_lit(exp)
|
1348
|
+
lit = exp.shift
|
1349
|
+
res = case lit
|
1350
|
+
when Fixnum, Bignum, Float
|
1351
|
+
lit.to_s
|
1352
|
+
when String
|
1353
|
+
lit.inspect
|
1354
|
+
when Symbol
|
1355
|
+
lit.to_s.inspect
|
1356
|
+
when Regexp
|
1357
|
+
lit.inspect
|
1358
|
+
when Range
|
1359
|
+
range = @model.lookup_constant('::Range')
|
1360
|
+
@model.add_method_call(m = @model.encode_method("new"))
|
1361
|
+
"#{range}.#{m}(#{@model.encode_nil},#{lit.first},#{lit.last},#{lit.exclude_end?})"
|
1362
|
+
else
|
1363
|
+
raise
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
resultify(res)
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
def is_numeric_literal(exp)
|
1370
|
+
type = exp[0]
|
1371
|
+
return false if exp[0] != :lit
|
1372
|
+
case exp[1]
|
1373
|
+
when Fixnum, Bignum, Float
|
1374
|
+
true
|
1375
|
+
else
|
1376
|
+
false
|
1377
|
+
end
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
#
|
1381
|
+
# EXPRESSION
|
1382
|
+
#
|
1383
|
+
# Process string literal
|
1384
|
+
#
|
1385
|
+
def process_str(exp)
|
1386
|
+
str = exp.shift
|
1387
|
+
resultify(str.inspect)
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
#
|
1391
|
+
# EXPRESSION
|
1392
|
+
#
|
1393
|
+
def process_true(exp)
|
1394
|
+
resultify("true")
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
#
|
1398
|
+
# EXPRESSION
|
1399
|
+
#
|
1400
|
+
def process_false(exp)
|
1401
|
+
resultify("false")
|
1402
|
+
end
|
1403
|
+
|
1404
|
+
#
|
1405
|
+
# EXPRESSION
|
1406
|
+
#
|
1407
|
+
def process_nil(exp)
|
1408
|
+
resultify(@model.encode_nil)
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
#
|
1412
|
+
# EXPRESSION
|
1413
|
+
#
|
1414
|
+
def process_self(exp)
|
1415
|
+
resultify(@model.encode_self)
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
#
|
1419
|
+
# EXPRESSION
|
1420
|
+
#
|
1421
|
+
def process_splat(exp)
|
1422
|
+
value = exp.shift
|
1423
|
+
to_splat = @model.encode_globalattr('to_splat')
|
1424
|
+
str = without_result do
|
1425
|
+
want_expression do
|
1426
|
+
"#{to_splat}(#{ process(value) })"
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
resultify(str)
|
1430
|
+
end
|
1431
|
+
|
1432
|
+
#
|
1433
|
+
# EXPRESSION
|
1434
|
+
#
|
1435
|
+
def process_argscat(exp)
|
1436
|
+
prefix = exp.shift
|
1437
|
+
splat = exp.shift
|
1438
|
+
raise unless prefix.first == :array
|
1439
|
+
to_splat = @model.encode_globalattr('to_splat')
|
1440
|
+
str = without_result do
|
1441
|
+
want_expression do
|
1442
|
+
"#{process(prefix)}.concat(#{to_splat}(#{ process(splat) }))"
|
1443
|
+
end
|
1444
|
+
end
|
1445
|
+
resultify(str)
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
#
|
1449
|
+
# EXPRESSION
|
1450
|
+
#
|
1451
|
+
# Array literal
|
1452
|
+
#
|
1453
|
+
def process_array(exp)
|
1454
|
+
str = without_result do
|
1455
|
+
want_expression do
|
1456
|
+
"[" + exp.map{|e| process(e)}.compact.join(",") + "]"
|
1457
|
+
end
|
1458
|
+
end
|
1459
|
+
exp.clear
|
1460
|
+
resultify(str)
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
#
|
1464
|
+
# EXPRESSION
|
1465
|
+
#
|
1466
|
+
# We box the hash and use our own implementation, as Javascript's
|
1467
|
+
# associative arrays are very different to Ruby hashes.
|
1468
|
+
#
|
1469
|
+
def process_hash(exp)
|
1470
|
+
kv_list = exp
|
1471
|
+
raise if kv_list.length % 2 != 0
|
1472
|
+
|
1473
|
+
args =
|
1474
|
+
without_result do
|
1475
|
+
want_expression do
|
1476
|
+
kv_list.map {|i| process(i)}.join(",")
|
1477
|
+
end
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
str = @model.lookup_constant('::Hash') + "."
|
1481
|
+
if kv_list.empty?
|
1482
|
+
# empty Hash
|
1483
|
+
@model.add_method_call(m = @model.encode_method("new"))
|
1484
|
+
str << "#{m}()"
|
1485
|
+
else
|
1486
|
+
@model.add_method_call(m = @model.encode_method("new_from_key_value_list"))
|
1487
|
+
str << "#{m}(#{@model.encode_nil},#{args})"
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
exp.clear
|
1491
|
+
|
1492
|
+
return str
|
1493
|
+
end
|
1494
|
+
|
1495
|
+
#
|
1496
|
+
# EXPRESSION
|
1497
|
+
#
|
1498
|
+
# Empty array.
|
1499
|
+
#
|
1500
|
+
def process_zarray(exp)
|
1501
|
+
resultify("[]")
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
|
1505
|
+
#
|
1506
|
+
# EXPRESSION
|
1507
|
+
#
|
1508
|
+
# Local variable assignment
|
1509
|
+
#
|
1510
|
+
# We have to write down all local variables, because they have to be
|
1511
|
+
# declared at the top of the method (otherwise they are seen as
|
1512
|
+
# "global" variables :)
|
1513
|
+
#
|
1514
|
+
def process_lasgn(exp)
|
1515
|
+
lvar = exp.shift
|
1516
|
+
value = exp.shift
|
1517
|
+
|
1518
|
+
lvar_name = @model.encode_local_variable(lvar)
|
1519
|
+
@local_variables.add(lvar_name)
|
1520
|
+
# local variables need not to be initialized, because they
|
1521
|
+
# have to be asigned to before usage, otherwise they would
|
1522
|
+
# not be local variables but methods instead.
|
1523
|
+
@local_variables_need_no_initialization.add(lvar_name)
|
1524
|
+
|
1525
|
+
str = without_result do
|
1526
|
+
want_expression do
|
1527
|
+
"#{lvar_name}=#{process(value)}"
|
1528
|
+
end
|
1529
|
+
end
|
1530
|
+
|
1531
|
+
resultify(str)
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
#
|
1535
|
+
# EXPRESSION
|
1536
|
+
#
|
1537
|
+
# Local variable lookup
|
1538
|
+
#
|
1539
|
+
def process_lvar(exp)
|
1540
|
+
lvar = exp.shift
|
1541
|
+
|
1542
|
+
lvar_name = @model.encode_local_variable(lvar)
|
1543
|
+
raise "variable not available" unless @local_variables.include?(lvar_name)
|
1544
|
+
|
1545
|
+
resultify("#{lvar_name}")
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
#
|
1549
|
+
# EXPRESSION
|
1550
|
+
#
|
1551
|
+
# Regular expression lookup $1 .. $9
|
1552
|
+
#
|
1553
|
+
def process_nth_ref(exp)
|
1554
|
+
n = exp.shift
|
1555
|
+
raise if n < 1 or n > 9
|
1556
|
+
resultify("(RegExp.$#{n} || #{@model.encode_nil})")
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
#
|
1560
|
+
# EXPRESSION
|
1561
|
+
#
|
1562
|
+
# Global variable lookup
|
1563
|
+
#
|
1564
|
+
def process_gvar(exp)
|
1565
|
+
gvar = exp.shift
|
1566
|
+
|
1567
|
+
res =
|
1568
|
+
case gvar.to_s
|
1569
|
+
when "$!"
|
1570
|
+
# this is a special variable which holds the current exception
|
1571
|
+
exception_name()
|
1572
|
+
else
|
1573
|
+
gvar_name = @model.encode_global_variable(gvar)
|
1574
|
+
"(typeof(#{gvar_name})=='undefined'?#{@model.encode_nil}:#{gvar_name})"
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
resultify(res)
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
#
|
1581
|
+
# EXPRESSION
|
1582
|
+
#
|
1583
|
+
# Global variable assignment
|
1584
|
+
#
|
1585
|
+
def process_gasgn(exp)
|
1586
|
+
gvar = exp.shift
|
1587
|
+
value = exp.shift
|
1588
|
+
|
1589
|
+
gvar_name = @model.encode_global_variable(gvar)
|
1590
|
+
|
1591
|
+
str = without_result do
|
1592
|
+
want_expression do
|
1593
|
+
"#{gvar_name}=#{process(value)}"
|
1594
|
+
end
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
resultify(str)
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
#
|
1601
|
+
# EXPRESSION
|
1602
|
+
#
|
1603
|
+
# Dynamic variable lookup
|
1604
|
+
#
|
1605
|
+
def process_dvar(exp)
|
1606
|
+
dvar = exp.shift
|
1607
|
+
dvar_name = @model.encode_local_variable(dvar)
|
1608
|
+
raise "dynamic variable not available" unless @current_iter_dvars.include?(dvar_name)
|
1609
|
+
|
1610
|
+
resultify("#{dvar_name}")
|
1611
|
+
end
|
1612
|
+
|
1613
|
+
#
|
1614
|
+
# EXPRESSION
|
1615
|
+
#
|
1616
|
+
# Dynamic variable assignment
|
1617
|
+
#
|
1618
|
+
def process_dasgn(exp)
|
1619
|
+
dvar = exp.shift
|
1620
|
+
value = exp.shift
|
1621
|
+
dvar_name = @model.encode_local_variable(dvar)
|
1622
|
+
raise "dynamic variable not available" unless @current_iter_dvars.include?(dvar_name)
|
1623
|
+
|
1624
|
+
str = without_result do
|
1625
|
+
want_expression do
|
1626
|
+
"#{dvar_name}=#{process(value)}"
|
1627
|
+
end
|
1628
|
+
end
|
1629
|
+
|
1630
|
+
resultify(str)
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
#
|
1634
|
+
# EXPRESSION
|
1635
|
+
#
|
1636
|
+
# Dynamic variable declaration and assignment
|
1637
|
+
#
|
1638
|
+
def process_dasgn_curr(exp)
|
1639
|
+
dvar = exp.shift
|
1640
|
+
value = exp.shift
|
1641
|
+
dvar_name = @model.encode_local_variable(dvar)
|
1642
|
+
|
1643
|
+
@current_iter_dvars.add(dvar_name)
|
1644
|
+
|
1645
|
+
str = without_result do
|
1646
|
+
want_expression do
|
1647
|
+
if value
|
1648
|
+
"#{dvar_name}=#{process(value)}"
|
1649
|
+
else
|
1650
|
+
# this should never happen, see process_iter()
|
1651
|
+
raise "no declarations possible"
|
1652
|
+
end
|
1653
|
+
end
|
1654
|
+
end
|
1655
|
+
|
1656
|
+
resultify(str)
|
1657
|
+
end
|
1658
|
+
|
1659
|
+
#
|
1660
|
+
# EXPRESSION
|
1661
|
+
#
|
1662
|
+
# Instance variable lookup
|
1663
|
+
#
|
1664
|
+
def process_ivar(exp)
|
1665
|
+
ivar = exp.shift
|
1666
|
+
ivar_name = @model.encode_instance_variable(ivar)
|
1667
|
+
@read_instance_variables.add(ivar_name)
|
1668
|
+
resultify("#{@model.encode_self}.#{ivar_name}")
|
1669
|
+
end
|
1670
|
+
|
1671
|
+
def process_svalue
|
1672
|
+
raise
|
1673
|
+
end
|
1674
|
+
|
1675
|
+
def process_to_ary(exp)
|
1676
|
+
value = exp.shift
|
1677
|
+
str = without_result do
|
1678
|
+
want_expression do
|
1679
|
+
case value.first
|
1680
|
+
when :lit
|
1681
|
+
"[" + process(value) + "]"
|
1682
|
+
when :array, :zarray
|
1683
|
+
process(value)
|
1684
|
+
when :special_to_ary
|
1685
|
+
# this is used inside of process_iter to build the multiple
|
1686
|
+
# assignment.
|
1687
|
+
value[1]
|
1688
|
+
else
|
1689
|
+
generate_method_call_receiver_exp(value, "to_ary", nil, nil)
|
1690
|
+
end
|
1691
|
+
end
|
1692
|
+
end
|
1693
|
+
resultify(str)
|
1694
|
+
end
|
1695
|
+
|
1696
|
+
#
|
1697
|
+
# EXPRESSION
|
1698
|
+
#
|
1699
|
+
# This is used to insert a pure inline JS string into the
|
1700
|
+
# code. It is used for example in process_masgn.
|
1701
|
+
#
|
1702
|
+
# It is not part of the ParseTree returned node types!
|
1703
|
+
#
|
1704
|
+
def process_special_inline_js_value(exp)
|
1705
|
+
return exp.shift
|
1706
|
+
end
|
1707
|
+
|
1708
|
+
#
|
1709
|
+
# EXPRESSION
|
1710
|
+
#
|
1711
|
+
# Multiple assignment
|
1712
|
+
#
|
1713
|
+
# Simple case:
|
1714
|
+
#
|
1715
|
+
# a, b = 1, 2
|
1716
|
+
#
|
1717
|
+
# [:masgn,
|
1718
|
+
# [:array, [:lasgn, :a], [:lasgn, :b]],
|
1719
|
+
# [:array, [:lit, 1], [:lit, 2]]]]]]
|
1720
|
+
#
|
1721
|
+
# Case with splat argument:
|
1722
|
+
#
|
1723
|
+
# a, *b = 1, 2, 3
|
1724
|
+
#
|
1725
|
+
# [:masgn,
|
1726
|
+
# [:array, [:lasgn, :a]],
|
1727
|
+
# [:lasgn, :b],
|
1728
|
+
# [:array, [:lit, 1], [:lit, 2], [:lit, 3]]]]]]
|
1729
|
+
#
|
1730
|
+
# Another case:
|
1731
|
+
#
|
1732
|
+
# a, b = 1
|
1733
|
+
#
|
1734
|
+
# [:masgn,
|
1735
|
+
# [:array, [:lasgn, :a], [:lasgn, :b]],
|
1736
|
+
# [:to_ary, [:lit, 1]]]
|
1737
|
+
#
|
1738
|
+
# We actually implement multiple assignment using a
|
1739
|
+
# temporary array. Example:
|
1740
|
+
#
|
1741
|
+
# a, b = b, a
|
1742
|
+
#
|
1743
|
+
# leads to the following javascript code
|
1744
|
+
#
|
1745
|
+
# (_t = [b,a],
|
1746
|
+
# a = _t[0] == null ? nil : _t[0],
|
1747
|
+
# b = _t[1] == null ? nil : _t[1])
|
1748
|
+
#
|
1749
|
+
# When a splat argument is given, there's just an
|
1750
|
+
# additional assignment which takes the rest of the
|
1751
|
+
# array.
|
1752
|
+
#
|
1753
|
+
def process_masgn(exp)
|
1754
|
+
lhs = exp.shift
|
1755
|
+
rhs = exp.pop
|
1756
|
+
splat = exp.shift
|
1757
|
+
|
1758
|
+
raise unless lhs.first == :array
|
1759
|
+
raise unless rhs.first == :array or rhs.first == :to_ary
|
1760
|
+
|
1761
|
+
want_expression do
|
1762
|
+
with_temporary_variable do |tmp|
|
1763
|
+
assgn = []
|
1764
|
+
without_result do
|
1765
|
+
assgn << "#{tmp}=#{process(rhs)}"
|
1766
|
+
|
1767
|
+
# lhs[0] == :array -> skip it
|
1768
|
+
lhs[1..-1].each_with_index do |assignment, i| # for example where assignment == [:lasgn, :a]
|
1769
|
+
assignment << s(:special_inline_js_value, "#{tmp}[#{i}]==null?#{@model.encode_nil}:#{tmp}[#{i}]")
|
1770
|
+
assgn << process(assignment)
|
1771
|
+
end
|
1772
|
+
|
1773
|
+
if splat
|
1774
|
+
# splat is for example [:lasgn, :a]
|
1775
|
+
splat << s(:special_inline_js_value, "#{tmp}.slice(#{lhs.size-1})")
|
1776
|
+
assgn << process(splat)
|
1777
|
+
end
|
1778
|
+
end
|
1779
|
+
|
1780
|
+
# return value of the expression is the array
|
1781
|
+
assgn << resultify("#{tmp}")
|
1782
|
+
|
1783
|
+
"(" + assgn.join(",") + ")"
|
1784
|
+
end
|
1785
|
+
end
|
1786
|
+
end
|
1787
|
+
|
1788
|
+
#
|
1789
|
+
# EXPRESSION
|
1790
|
+
#
|
1791
|
+
# Instance variable assignment
|
1792
|
+
#
|
1793
|
+
def process_iasgn(exp)
|
1794
|
+
ivar = exp.shift
|
1795
|
+
value = exp.shift
|
1796
|
+
ivar_name = @model.encode_instance_variable(ivar)
|
1797
|
+
|
1798
|
+
str = without_result do
|
1799
|
+
want_expression do
|
1800
|
+
"#{@model.encode_self}.#{ivar_name}=#{process(value)}"
|
1801
|
+
end
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
resultify(str)
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
#
|
1808
|
+
# EXPRESSION (doesn't generate any code directly!)
|
1809
|
+
#
|
1810
|
+
# Iterator block.
|
1811
|
+
#
|
1812
|
+
# We process as follows:
|
1813
|
+
#
|
1814
|
+
# 1. Generate code for the arguments of the block (multiple
|
1815
|
+
# assignment etc.)
|
1816
|
+
#
|
1817
|
+
# 2. Generate code for the code block.
|
1818
|
+
#
|
1819
|
+
# 3. In 1 and 2 we also write down all dynamic variable assignments.
|
1820
|
+
# ("dasgn_curr" which introduces a new scope)
|
1821
|
+
#
|
1822
|
+
# 4. Generate code for variable declarations.
|
1823
|
+
#
|
1824
|
+
# TODO: assign arity to function object!
|
1825
|
+
#
|
1826
|
+
def process_iter(exp)
|
1827
|
+
call = exp.shift # the method call to which the iterator is passed
|
1828
|
+
params = exp.shift # arguments of the block
|
1829
|
+
code = exp.shift # the code block
|
1830
|
+
|
1831
|
+
old_iter_dvars = @current_iter_dvars
|
1832
|
+
@current_iter_dvars = Set.new
|
1833
|
+
@block_whileloop_stack.push(:iter)
|
1834
|
+
|
1835
|
+
# Get an argument name for the iterator function signature.
|
1836
|
+
arg_name = @model.encode_fresh_local_variable()
|
1837
|
+
|
1838
|
+
fun_str = ""
|
1839
|
+
asgn_str = ""
|
1840
|
+
|
1841
|
+
if params.nil?
|
1842
|
+
# Case 1: {}: Any number of arguments may be passed
|
1843
|
+
arity = -1
|
1844
|
+
fun_str << "function(){"
|
1845
|
+
elsif params == 0
|
1846
|
+
# Case 2: {||}: Zero arguments
|
1847
|
+
arity = 0
|
1848
|
+
fun_str << "function(){"
|
1849
|
+
elsif params.first == :masgn
|
1850
|
+
# Case 3: {|i,j|}: Multiple arguments (multiple assignment)
|
1851
|
+
arity = nil # |arity| >= 2
|
1852
|
+
fun_str << "function(#{arg_name}){"
|
1853
|
+
|
1854
|
+
# TODO: remove masgn_iter and instead put the corresponding logic
|
1855
|
+
# into yield!
|
1856
|
+
masgn_iter = @model.encode_globalattr("masgn_iter")
|
1857
|
+
params << [:to_ary, [:special_to_ary, "#{masgn_iter}(#{arg_name})"]]
|
1858
|
+
want_expression(false) do
|
1859
|
+
without_result do
|
1860
|
+
asgn_str << process(params)
|
1861
|
+
end
|
1862
|
+
end
|
1863
|
+
else
|
1864
|
+
# Case 4: {|i|}: Single argument
|
1865
|
+
# TODO: warning if not exactly one argument is passed
|
1866
|
+
arity = 1
|
1867
|
+
fun_str << "function(#{arg_name}){"
|
1868
|
+
|
1869
|
+
# we convert a single argument into a multiple assignment clause
|
1870
|
+
# with one argument.
|
1871
|
+
|
1872
|
+
sasgn_iter = @model.encode_globalattr("sasgn_iter")
|
1873
|
+
params << [:special_inline_js_value, "#{arg_name}==null?#{@model.encode_nil}:#{arg_name}"]
|
1874
|
+
|
1875
|
+
want_expression(false) do
|
1876
|
+
without_result do
|
1877
|
+
asgn_str << process(params)
|
1878
|
+
end
|
1879
|
+
end
|
1880
|
+
end
|
1881
|
+
|
1882
|
+
old_result_name = @result_name
|
1883
|
+
@result_name = nil
|
1884
|
+
|
1885
|
+
block_str = ""
|
1886
|
+
want_expression(false) do
|
1887
|
+
want_result do
|
1888
|
+
block_str << process(code) if code
|
1889
|
+
end
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
# generate code for the variable declarations
|
1893
|
+
var_str = ""
|
1894
|
+
unless @current_iter_dvars.empty?
|
1895
|
+
var_str << "var "
|
1896
|
+
var_str << @current_iter_dvars.to_a.join(",")
|
1897
|
+
var_str << sep()
|
1898
|
+
end
|
1899
|
+
|
1900
|
+
# declare and initialize the return value
|
1901
|
+
if @result_name
|
1902
|
+
var_str << "var #{@result_name}=#{@model.encode_nil}#{sep()}"
|
1903
|
+
end
|
1904
|
+
|
1905
|
+
# NOTE: we don't need to initialize any dvar to nil, as
|
1906
|
+
# they can't be used before being assigned to (because
|
1907
|
+
# otherwise they are vcall's and not dynamic variables).
|
1908
|
+
|
1909
|
+
str = fun_str + var_str + asgn_str + sep() + block_str
|
1910
|
+
str << "#{sep()}return #{@result_name}" if @result_name
|
1911
|
+
str << "}"
|
1912
|
+
|
1913
|
+
put_iter(str)
|
1914
|
+
|
1915
|
+
@result_name = old_result_name
|
1916
|
+
@current_iter_dvars = old_iter_dvars
|
1917
|
+
@block_whileloop_stack.pop || raise
|
1918
|
+
|
1919
|
+
return process(call)
|
1920
|
+
end
|
1921
|
+
|
1922
|
+
#
|
1923
|
+
# a =~ /regexp/
|
1924
|
+
#
|
1925
|
+
# is converted to
|
1926
|
+
#
|
1927
|
+
# [:match3, [:lit, /regexp/], [:lvar, :a]]
|
1928
|
+
#
|
1929
|
+
# We just convert it to:
|
1930
|
+
#
|
1931
|
+
# [:call, [:lvar, :a], :=~, [:array, [:lit, /regexp/]]]
|
1932
|
+
#
|
1933
|
+
def process_match3(exp)
|
1934
|
+
right = exp.shift
|
1935
|
+
left = exp.shift
|
1936
|
+
return process(s(:call, left, :=~, s(:array, right)))
|
1937
|
+
end
|
1938
|
+
|
1939
|
+
#
|
1940
|
+
# if /regexp/ =~ a
|
1941
|
+
#
|
1942
|
+
# is converted to
|
1943
|
+
#
|
1944
|
+
# [:match2, [:lit, /regexp/], [:lvar, :a]]
|
1945
|
+
#
|
1946
|
+
# We just convert it to:
|
1947
|
+
#
|
1948
|
+
# [:call, [:lit, /regexp/], :=~, [:array, [:lvar, :a]]]
|
1949
|
+
#
|
1950
|
+
def process_match2(exp)
|
1951
|
+
left = exp.shift
|
1952
|
+
right = exp.shift
|
1953
|
+
return process(s(:call, left, :=~, s(:array, right)))
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
#######################################################################
|
1957
|
+
|
1958
|
+
private
|
1959
|
+
|
1960
|
+
#######################################################################
|
1961
|
+
|
1962
|
+
def without_result(&block)
|
1963
|
+
want_result(false, &block)
|
1964
|
+
end
|
1965
|
+
|
1966
|
+
def want_result(wish=true)
|
1967
|
+
old_want_result = @want_result
|
1968
|
+
begin
|
1969
|
+
@want_result = wish
|
1970
|
+
return yield
|
1971
|
+
ensure
|
1972
|
+
@want_result = old_want_result
|
1973
|
+
end
|
1974
|
+
end
|
1975
|
+
|
1976
|
+
def resultify(str)
|
1977
|
+
if @want_result
|
1978
|
+
# FIXME
|
1979
|
+
result_name() + "=" + str
|
1980
|
+
else
|
1981
|
+
str
|
1982
|
+
end
|
1983
|
+
end
|
1984
|
+
|
1985
|
+
def result_name
|
1986
|
+
@result_name ||= @model.encode_fresh_local_variable()
|
1987
|
+
#@local_variables.add(@result_name)
|
1988
|
+
@result_name
|
1989
|
+
end
|
1990
|
+
|
1991
|
+
#
|
1992
|
+
# Generate a name for @block_name (which is the argument for
|
1993
|
+
# the block in the Javascript function signature).
|
1994
|
+
#
|
1995
|
+
def block_name
|
1996
|
+
@block_name ||= @model.encode_fresh_local_variable()
|
1997
|
+
@argument_variables.add(@block_name)
|
1998
|
+
@iterators_used = true
|
1999
|
+
# FIXME: not true. passing an interator &block does not
|
2000
|
+
# mean that iterators are used!
|
2001
|
+
@block_name
|
2002
|
+
end
|
2003
|
+
|
2004
|
+
def arguments_name()
|
2005
|
+
@arguments_name ||= @model.encode_fresh_local_variable()
|
2006
|
+
end
|
2007
|
+
|
2008
|
+
def exception_name()
|
2009
|
+
@exception_name ||= @model.encode_fresh_local_variable()
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
def conditionalize(exp, negate=false)
|
2013
|
+
# Optimize literals
|
2014
|
+
case exp.first
|
2015
|
+
when :true, :lit, :self, :str # evaluate to true
|
2016
|
+
return (negate ? "false" : "true")
|
2017
|
+
when :false, :nil # evaluate to false
|
2018
|
+
return (negate ? "true" : "false")
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
want_expression do
|
2022
|
+
with_temporary_variable do |tmp|
|
2023
|
+
@local_variables_need_no_initialization.add(tmp)
|
2024
|
+
if negate
|
2025
|
+
"(#{tmp}=#{process(exp)},#{tmp}===false||#{tmp}===nil)"
|
2026
|
+
else
|
2027
|
+
"(#{tmp}=#{process(exp)},#{tmp}!==false&&#{tmp}!==nil)"
|
2028
|
+
end
|
2029
|
+
end
|
2030
|
+
end
|
2031
|
+
end
|
2032
|
+
|
2033
|
+
def with_temporary_variable
|
2034
|
+
var = get_temporary_variable()
|
2035
|
+
begin
|
2036
|
+
return (yield var)
|
2037
|
+
ensure
|
2038
|
+
put_temporary_variable(var)
|
2039
|
+
end
|
2040
|
+
end
|
2041
|
+
|
2042
|
+
def get_temporary_variable
|
2043
|
+
tmp = @temporary_variables_pool.shift || @model.encode_fresh_local_variable
|
2044
|
+
@local_variables.add(tmp)
|
2045
|
+
return tmp
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
def put_temporary_variable(tmp)
|
2049
|
+
@temporary_variables_pool.unshift(tmp)
|
2050
|
+
end
|
2051
|
+
|
2052
|
+
def get_iter
|
2053
|
+
res = @iter
|
2054
|
+
@iter = nil
|
2055
|
+
res
|
2056
|
+
end
|
2057
|
+
|
2058
|
+
def put_iter(iter)
|
2059
|
+
@iter = iter
|
2060
|
+
end
|
2061
|
+
end
|