rubyjs 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/README +128 -0
  2. data/Rakefile +9 -0
  3. data/bin/rubyjs +140 -0
  4. data/examples/ex1/Rakefile +7 -0
  5. data/examples/ex1/ex1.js +771 -0
  6. data/examples/ex1/ex1.rb +42 -0
  7. data/examples/ex1/index.html +7 -0
  8. data/examples/hw/Rakefile +9 -0
  9. data/examples/hw/hw.js +635 -0
  10. data/examples/hw/hw.rb +7 -0
  11. data/examples/hw/index.html +7 -0
  12. data/patches/parse_tree.rb.diff +34 -0
  13. data/rubyjs.gemspec +24 -0
  14. data/src/rubyjs.rb +4 -0
  15. data/src/rubyjs/code_generator.rb +474 -0
  16. data/src/rubyjs/compiler.rb +2007 -0
  17. data/src/rubyjs/debug_name_generator.rb +75 -0
  18. data/src/rubyjs/encoder.rb +171 -0
  19. data/src/rubyjs/eval_into.rb +59 -0
  20. data/src/rubyjs/lib/core.rb +1008 -0
  21. data/src/rubyjs/lib/json.rb +101 -0
  22. data/src/rubyjs/model.rb +287 -0
  23. data/src/rubyjs/name_generator.rb +71 -0
  24. data/src/rwt/AbsolutePanel.rb +161 -0
  25. data/src/rwt/DOM.Konqueror.rb +89 -0
  26. data/src/rwt/DOM.Opera.rb +65 -0
  27. data/src/rwt/DOM.rb +1044 -0
  28. data/src/rwt/Event.Opera.rb +35 -0
  29. data/src/rwt/Event.rb +429 -0
  30. data/src/rwt/HTTPRequest.IE6.rb +5 -0
  31. data/src/rwt/HTTPRequest.rb +74 -0
  32. data/src/rwt/Label.rb +164 -0
  33. data/src/rwt/Panel.rb +90 -0
  34. data/src/rwt/RootPanel.rb +16 -0
  35. data/src/rwt/UIObject.rb +495 -0
  36. data/src/rwt/Widget.rb +193 -0
  37. data/src/rwt/ported-from/AbsolutePanel.java +158 -0
  38. data/src/rwt/ported-from/DOM.java +571 -0
  39. data/src/rwt/ported-from/DOMImpl.java +426 -0
  40. data/src/rwt/ported-from/DOMImplOpera.java +82 -0
  41. data/src/rwt/ported-from/DOMImplStandard.java +234 -0
  42. data/src/rwt/ported-from/HTTPRequest.java +81 -0
  43. data/src/rwt/ported-from/HTTPRequestImpl.java +103 -0
  44. data/src/rwt/ported-from/Label.java +163 -0
  45. data/src/rwt/ported-from/Panel.java +99 -0
  46. data/src/rwt/ported-from/UIObject.java +614 -0
  47. data/src/rwt/ported-from/Widget.java +221 -0
  48. data/test/benchmark/bm_call_conv1.js +16 -0
  49. data/test/benchmark/bm_call_conv2.js +14 -0
  50. data/test/benchmark/bm_var_acc1.js +13 -0
  51. data/test/benchmark/bm_var_acc2.js +11 -0
  52. data/test/benchmark/bm_vm1_block.js +15 -0
  53. data/test/benchmark/bm_vm1_block.rb +15 -0
  54. data/test/benchmark/bm_vm1_const.js +13 -0
  55. data/test/benchmark/bm_vm1_const.rb +13 -0
  56. data/test/benchmark/bm_vm1_ensure.js +17 -0
  57. data/test/benchmark/bm_vm1_ensure.rb +15 -0
  58. data/test/benchmark/common.js +4 -0
  59. data/test/benchmark/common.rb +5 -0
  60. data/test/benchmark/params.yaml +7 -0
  61. data/test/browser.test.html +4059 -0
  62. data/test/browser.test.js +3225 -0
  63. data/test/common.Browser.rb +13 -0
  64. data/test/common.rb +8 -0
  65. data/test/gen_browser_test_suite.rb +129 -0
  66. data/test/gen_test_suite.rb +41 -0
  67. data/test/run_benchs.rb +58 -0
  68. data/test/run_tests.rb +22 -0
  69. data/test/test_args.rb +24 -0
  70. data/test/test_array.rb +26 -0
  71. data/test/test_case.rb +35 -0
  72. data/test/test_class.rb +55 -0
  73. data/test/test_eql.rb +9 -0
  74. data/test/test_exception.rb +61 -0
  75. data/test/test_expr.rb +12 -0
  76. data/test/test_hash.rb +29 -0
  77. data/test/test_if.rb +28 -0
  78. data/test/test_inspect.rb +10 -0
  79. data/test/test_lebewesen.rb +39 -0
  80. data/test/test_massign.rb +66 -0
  81. data/test/test_new.rb +12 -0
  82. data/test/test_range.rb +53 -0
  83. data/test/test_regexp.rb +22 -0
  84. data/test/test_send.rb +65 -0
  85. data/test/test_simple_output.rb +5 -0
  86. data/test/test_splat.rb +21 -0
  87. data/test/test_string.rb +51 -0
  88. data/test/test_yield.rb +152 -0
  89. data/utils/js/Makefile +9 -0
  90. data/utils/js/RunScript.class +0 -0
  91. data/utils/js/RunScript.java +73 -0
  92. data/utils/js/js.jar +0 -0
  93. data/utils/js/run.sh +3 -0
  94. data/utils/jsc/Makefile +7 -0
  95. data/utils/jsc/README +3 -0
  96. data/utils/jsc/RunScript.c +93 -0
  97. data/utils/jsc/run.sh +15 -0
  98. data/utils/yuicompressor/README +1 -0
  99. data/utils/yuicompressor/yuicompressor-2.2.5.jar +0 -0
  100. data/vendor/ParseTree-1.7.1-patched/History.txt +217 -0
  101. data/vendor/ParseTree-1.7.1-patched/Manifest.txt +22 -0
  102. data/vendor/ParseTree-1.7.1-patched/README.txt +110 -0
  103. data/vendor/ParseTree-1.7.1-patched/Rakefile +41 -0
  104. data/vendor/ParseTree-1.7.1-patched/bin/parse_tree_abc +89 -0
  105. data/vendor/ParseTree-1.7.1-patched/bin/parse_tree_audit +28 -0
  106. data/vendor/ParseTree-1.7.1-patched/bin/parse_tree_deps +62 -0
  107. data/vendor/ParseTree-1.7.1-patched/bin/parse_tree_show +49 -0
  108. data/vendor/ParseTree-1.7.1-patched/demo/printer.rb +20 -0
  109. data/vendor/ParseTree-1.7.1-patched/lib/composite_sexp_processor.rb +49 -0
  110. data/vendor/ParseTree-1.7.1-patched/lib/parse_tree.rb +1013 -0
  111. data/vendor/ParseTree-1.7.1-patched/lib/sexp.rb +235 -0
  112. data/vendor/ParseTree-1.7.1-patched/lib/sexp_processor.rb +330 -0
  113. data/vendor/ParseTree-1.7.1-patched/lib/unique.rb +15 -0
  114. data/vendor/ParseTree-1.7.1-patched/test/pt_testcase.rb +1221 -0
  115. data/vendor/ParseTree-1.7.1-patched/test/something.rb +53 -0
  116. data/vendor/ParseTree-1.7.1-patched/test/test_all.rb +13 -0
  117. data/vendor/ParseTree-1.7.1-patched/test/test_composite_sexp_processor.rb +69 -0
  118. data/vendor/ParseTree-1.7.1-patched/test/test_parse_tree.rb +216 -0
  119. data/vendor/ParseTree-1.7.1-patched/test/test_sexp.rb +291 -0
  120. data/vendor/ParseTree-1.7.1-patched/test/test_sexp_processor.rb +244 -0
  121. data/vendor/ParseTree-1.7.1-patched/validate.sh +31 -0
  122. data/vendor/ParseTree-1.7.1/History.txt +217 -0
  123. data/vendor/ParseTree-1.7.1/Manifest.txt +22 -0
  124. data/vendor/ParseTree-1.7.1/README.txt +110 -0
  125. data/vendor/ParseTree-1.7.1/Rakefile +41 -0
  126. data/vendor/ParseTree-1.7.1/bin/parse_tree_abc +89 -0
  127. data/vendor/ParseTree-1.7.1/bin/parse_tree_audit +28 -0
  128. data/vendor/ParseTree-1.7.1/bin/parse_tree_deps +62 -0
  129. data/vendor/ParseTree-1.7.1/bin/parse_tree_show +49 -0
  130. data/vendor/ParseTree-1.7.1/demo/printer.rb +20 -0
  131. data/vendor/ParseTree-1.7.1/lib/composite_sexp_processor.rb +49 -0
  132. data/vendor/ParseTree-1.7.1/lib/parse_tree.rb +1004 -0
  133. data/vendor/ParseTree-1.7.1/lib/sexp.rb +235 -0
  134. data/vendor/ParseTree-1.7.1/lib/sexp_processor.rb +330 -0
  135. data/vendor/ParseTree-1.7.1/lib/unique.rb +15 -0
  136. data/vendor/ParseTree-1.7.1/test/pt_testcase.rb +1221 -0
  137. data/vendor/ParseTree-1.7.1/test/something.rb +53 -0
  138. data/vendor/ParseTree-1.7.1/test/test_all.rb +13 -0
  139. data/vendor/ParseTree-1.7.1/test/test_composite_sexp_processor.rb +69 -0
  140. data/vendor/ParseTree-1.7.1/test/test_parse_tree.rb +216 -0
  141. data/vendor/ParseTree-1.7.1/test/test_sexp.rb +291 -0
  142. data/vendor/ParseTree-1.7.1/test/test_sexp_processor.rb +244 -0
  143. data/vendor/ParseTree-1.7.1/validate.sh +31 -0
  144. metadata +230 -0
@@ -0,0 +1,2007 @@
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
+ # EXPRESSION
630
+ #
631
+ # Attribute assignment: receiver.attr=(value)
632
+ #
633
+ # Same as a method call!
634
+ #
635
+ def process_attrasgn(exp)
636
+ process_call(exp)
637
+ end
638
+
639
+ #
640
+ # The ||= operator.
641
+ #
642
+ # For example:
643
+ #
644
+ # a = a || "hallo"
645
+ #
646
+ # [:lasgn, :a, [:or, [:lvar, :a], [:str, "hallo"]]],
647
+ #
648
+ # a ||= "hallo"
649
+ #
650
+ # [:op_asgn_or, [:lvar, :a], [:lasgn, :a, [:str, "hallo"]]]]]]
651
+ #
652
+ # We rewrite the one to the other.
653
+ #
654
+ def process_op_asgn_or(exp)
655
+ ref = exp.shift
656
+ asgn = exp.shift
657
+ asgn[2] = [:or, ref, asgn[2]]
658
+ process(asgn)
659
+ end
660
+
661
+ #
662
+ # EXPRESSION
663
+ #
664
+ # Method call with receiver
665
+ #
666
+ def process_call(exp)
667
+ receiver = exp.shift
668
+ method = exp.shift
669
+ args = exp.shift
670
+
671
+ iter = get_iter()
672
+
673
+ #
674
+ # RubyJS::inline("str")
675
+ #
676
+ # A different form of Javascript inline. We have this form
677
+ # because ``-style Javascript-inline has a different esacping
678
+ # of \. Sometimes we want no escaping, so
679
+ #
680
+ # RubyJS::inline %q(...) could be used.
681
+ #
682
+ if receiver == [:const, :RubyJS] and method == :inline and
683
+ args[0] == :array and args.size == 2 and args[1].size == 2 and args[1][0] == :str and iter.nil?
684
+ raise if @want_expression
685
+ return @model.interpolate(args[1][1])
686
+ end
687
+
688
+ str =
689
+ if $RUBYJS__OPTS.include?('OptimizeArithOps') and %w(> >= < <= + - * /).include?(method.to_s) and
690
+ iter.nil? and args and args[0] == :array and args.size == 2
691
+ without_result do
692
+ want_expression do
693
+ "(#{process(receiver)})#{method.to_s}(#{process(args[1])})"
694
+ end
695
+ end
696
+ else
697
+ without_result do
698
+ generate_method_call_receiver_exp(receiver, method, iter, args)
699
+ end
700
+ end
701
+ resultify(str)
702
+ end
703
+
704
+ #
705
+ # EXPRESSION
706
+ #
707
+ # Virtual call. Either a method call or a variable, determined at
708
+ # runtime.
709
+ #
710
+ # Ruby cannot distinguish a local variable and a method called without
711
+ # parentheses and without receiver at parse time. Example:
712
+ #
713
+ # def my_method
714
+ # method_or_not.inspect
715
+ # end
716
+ #
717
+ # If a local variable of that name exists, then it is used as
718
+ # receiver. Otherwise it is seen as a method and is called.
719
+ #
720
+ # NOTE: A vcall can never have an iterator! Because if an iterator is
721
+ # specified it is automatically no variable and as such a fcall or
722
+ # call is generated.
723
+ #
724
+ # NOTE: As RubyJS disallows to use eval(), a vcall can never be a
725
+ # local variable (FIXME: to be prooved).
726
+ #
727
+ def process_vcall(exp)
728
+ method = exp.shift
729
+
730
+ resultify(generate_method_call(@model.encode_self, method, nil, nil))
731
+ end
732
+
733
+ #
734
+ # EXPRESSION
735
+ #
736
+ # Q: Always call with "nil" if no argument is given?
737
+ # A: No, because then we could not distinguish between
738
+ # no arguments and "nil" as argument.
739
+ #
740
+ def process_yield(exp)
741
+ value = exp.shift
742
+
743
+ str = without_result do
744
+ want_expression do
745
+ block_name() + "(" + (value ? process(value) : '') + ")"
746
+ end
747
+ end
748
+ resultify(str)
749
+ end
750
+
751
+ #
752
+ # Constant lookup
753
+ # ===============
754
+ #
755
+ # Constant lookup in RubyJS is performed statically. This is possible
756
+ # because the whole class hierarchy is available at compile-time.
757
+
758
+ #
759
+ # EXPRESSION
760
+ #
761
+ def process_const(exp)
762
+ resultify(@model.lookup_constant(constify(exp, :const)))
763
+ end
764
+
765
+ #
766
+ # helper methods
767
+ #
768
+
769
+ def constify(exp, type)
770
+ exp.unshift(type)
771
+ const_rec(exp).join("::")
772
+ end
773
+
774
+ def const_rec(exp)
775
+ case exp.shift
776
+ when :const
777
+ return [exp.shift]
778
+ when :colon2
779
+ const_rec(exp.shift) + [exp.shift]
780
+ when :colon3
781
+ [nil, exp.shift]
782
+ else
783
+ raise
784
+ end
785
+ end
786
+
787
+ #
788
+ # EXPRESSION
789
+ #
790
+ # A::B # => [:colon2, [:const, :A], :B]
791
+ #
792
+ def process_colon2(exp)
793
+ resultify(@model.lookup_constant(constify(exp, :colon2)))
794
+ end
795
+
796
+ #
797
+ # EXPRESSION
798
+ #
799
+ # ::A # => [:colon3, :A]
800
+ #
801
+ def process_colon3(exp)
802
+ resultify(@model.lookup_constant(constify(exp, :colon3)))
803
+ end
804
+
805
+ #
806
+ # EXPRESSION
807
+ #
808
+ # Implements the "&&" operator, which has short circuit behaviour.
809
+ #
810
+ # a && b
811
+ #
812
+ # is equivalent in Ruby to
813
+ #
814
+ # if a then b else a end
815
+ #
816
+ def process_and(exp)
817
+ a = exp.shift
818
+ b = exp.shift
819
+
820
+ res = without_result do
821
+ want_expression do
822
+ with_temporary_variable do |tmp|
823
+ @local_variables_need_no_initialization.add(tmp)
824
+ "(#{tmp}=#{process(a)}, (#{tmp}!==false&&#{tmp}!==nil) ? (#{process(b)}) : #{tmp})"
825
+ end
826
+ end
827
+ end
828
+
829
+ return resultify(res)
830
+ end
831
+
832
+ #
833
+ # EXPRESSION
834
+ #
835
+ # Implements the "||" operator, which has short circuit behaviour.
836
+ #
837
+ # a || b
838
+ #
839
+ # is equivalent in Ruby to
840
+ #
841
+ # if a then a else b end
842
+ #
843
+ def process_or(exp)
844
+ a = exp.shift
845
+ b = exp.shift
846
+
847
+ res = without_result do
848
+ want_expression do
849
+ with_temporary_variable do |tmp|
850
+ @local_variables_need_no_initialization.add(tmp)
851
+ "(#{tmp}=#{process(a)}, (#{tmp}!==false&&#{tmp}!==nil) ? #{tmp} : (#{process(b)}))"
852
+ end
853
+ end
854
+ end
855
+
856
+ return resultify(res)
857
+ end
858
+
859
+ #
860
+ # EXPRESSION
861
+ #
862
+ def process_not(exp)
863
+ a = exp.shift
864
+
865
+ without_result do
866
+ want_expression do
867
+ a = conditionalize(a, true)
868
+ end
869
+ end
870
+
871
+ return resultify(a)
872
+ end
873
+
874
+ #
875
+ # STATEMENT/EXPRESSION
876
+ #
877
+ # unless is converted by the Ruby parser into an "if":
878
+ #
879
+ # unless X
880
+ # block
881
+ # end
882
+ #
883
+ # =>
884
+ #
885
+ # if X
886
+ # else
887
+ # block
888
+ # end
889
+ #
890
+ def process_if(exp)
891
+ cond = exp.shift
892
+ _then = exp.shift
893
+ _else = exp.shift
894
+
895
+ _then_processed = if _then
896
+ process(_then)
897
+ else
898
+ nil
899
+ end
900
+
901
+ _else_processed = if _else
902
+ process(_else)
903
+ else
904
+ nil
905
+ end
906
+
907
+ negate = false
908
+
909
+ if _then_processed.nil?
910
+ # no "then" block as in
911
+ #
912
+ # if X
913
+ # else
914
+ # block
915
+ # end
916
+ #
917
+ # => convert into
918
+ #
919
+ # if not X
920
+ # block
921
+ # end
922
+ #
923
+ # by swapping then and else blocks
924
+ # and negating the condition
925
+
926
+ _then_processed = _else_processed
927
+ _else_processed = nil
928
+ negate = true
929
+ end
930
+
931
+ cond_processed = without_result do
932
+ conditionalize(cond, negate)
933
+ end
934
+
935
+ str = ""
936
+
937
+ if @want_expression
938
+ _then_processed ||= resultify(@model.encode_nil)
939
+ _else_processes ||= resultify(@model.encode_nil)
940
+ str << "(#{cond_processed}?#{_then_processed}:#{_else_processed})"
941
+ else
942
+ str << "if(#{cond_processed}){"
943
+ str << (_then_processed || (@want_result ? resultify(@model.encode_nil) : ''))
944
+ str << "}"
945
+ if @want_result
946
+ _else_processed ||= resultify(@model.encode_nil)
947
+ end
948
+ if _else_processed
949
+ str << "else{"
950
+ str << _else_processed
951
+ str << "}"
952
+ end
953
+ end
954
+
955
+ return str
956
+ end
957
+
958
+ #
959
+ # STATEMENT/EXPRESSION
960
+ #
961
+ # case obj
962
+ # when c1
963
+ # block1
964
+ # when c2, c3
965
+ # block23
966
+ # else
967
+ # blockelse
968
+ # end
969
+ #
970
+ # [:case,
971
+ # obj,
972
+ # [:when, [:array, c1], block1],
973
+ # [:when, [:array, c2, c3], block23],
974
+ # blockelse or nil
975
+ # ]
976
+ #
977
+ # We are transforming the "case" into "if".
978
+ #
979
+ # _tmp = obj;
980
+ # if c1 === _tmp
981
+ # block1
982
+ # else
983
+ # if c2 === _tmp or c3 === _tmp then
984
+ # block23
985
+ # else
986
+ # blockelse
987
+ # end
988
+ # end
989
+ #
990
+ def process_case(exp)
991
+ obj = exp.shift
992
+
993
+ with_temporary_variable do |tmp|
994
+ @local_variables_need_no_initialization.add(tmp)
995
+ asgn = want_expression do
996
+ "#{tmp}=#{process(obj)}"
997
+ end
998
+
999
+ new_exp = current_exp = []
1000
+
1001
+ while not exp.empty?
1002
+ _when = exp.shift
1003
+ if _when.nil? or _when.first != :when
1004
+ # last element
1005
+ raise exp.inspect unless exp.empty?
1006
+ current_exp << _when
1007
+ else
1008
+ conds = _when[1]
1009
+ block = _when[2]
1010
+ raise unless conds.first == :array
1011
+
1012
+ cond = multi_or(conds[1..-1].map {|c| [:call, c, :===, [:array, [:special_inline_js_value, tmp]]] })
1013
+
1014
+ my_exp = [:if, cond, block]
1015
+
1016
+ current_exp << my_exp
1017
+ current_exp = my_exp
1018
+ end
1019
+ end
1020
+
1021
+ if_code = process(new_exp.first)
1022
+
1023
+ code =
1024
+ if @want_expression
1025
+ "(#{asgn}, #{if_code})"
1026
+ else
1027
+ "#{asgn}#{sep()}#{if_code}"
1028
+ end
1029
+
1030
+ code
1031
+ end
1032
+ end
1033
+
1034
+ def multi_or(exprs)
1035
+ case exprs.size
1036
+ when 0
1037
+ raise
1038
+ when 1
1039
+ # multi_or([1]) => 1
1040
+ exprs[0]
1041
+ when 2
1042
+ # multi_or([1, 2]) => [:or, 1, 2]
1043
+ [:or, exprs[0], exprs[1]]
1044
+ else
1045
+ # multi_or([1, 2, 3]) => [:or, 1, multi_or([2,3])] == [:or, 1, [:or, 2, 3]]
1046
+ [:or, exprs[0], multi_or(exprs[1..-1])]
1047
+ end
1048
+ end
1049
+
1050
+ #
1051
+ # STATEMENT
1052
+ #
1053
+ def process_begin(exp)
1054
+ raise if @want_expression
1055
+ block = exp.shift
1056
+ process(block)
1057
+ end
1058
+
1059
+ #
1060
+ # STATEMENT
1061
+ #
1062
+ def process_rescue(exp)
1063
+ raise if @want_expression
1064
+ block = exp.shift
1065
+ handler = exp.shift
1066
+ else_handler = exp.shift
1067
+ raise unless handler.first == :resbody
1068
+
1069
+ x = exception_name()
1070
+
1071
+ code = [:block, block]
1072
+ code << else_handler if else_handler
1073
+
1074
+ str = ""
1075
+ str << "try{#{process(code)}}"
1076
+ str << "catch(#{x}){"
1077
+
1078
+ # check whether it's an iterator break. if yes, pass it on
1079
+ iter_jump = @model.encode_globalattr("iter_jump")
1080
+ str << "if(#{x} instanceof #{iter_jump})throw(#{x})#{sep()}"
1081
+ str << "#{process(handler)}"
1082
+ str << "}"
1083
+
1084
+ str
1085
+ end
1086
+
1087
+ #
1088
+ # STATEMENT
1089
+ #
1090
+ def process_resbody(exp)
1091
+ raise if @want_expression
1092
+ condition = exp.shift
1093
+ if_body = exp.shift
1094
+ else_body = exp.shift
1095
+
1096
+ x = exception_name()
1097
+
1098
+ # if only "rescue" is used (and not e.g. "rescue Exception")
1099
+ condition ||= [:array, [:const, :StandardError]]
1100
+ raise unless condition.first == :array
1101
+
1102
+ # if this is the last resbody (else_body is nil) then
1103
+ # the exception couldn't be catched and we rethrow it!
1104
+ else_body ||= [:special_inline_js_value, "throw(#{x})"]
1105
+
1106
+ # build condition expression
1107
+ cond =
1108
+ multi_or(condition[1..-1].map do |c|
1109
+ [:call, c, :===, [:array, [:special_inline_js_value, x]]]
1110
+ end)
1111
+
1112
+ process([:if, cond, if_body, else_body])
1113
+ end
1114
+
1115
+ #
1116
+ # STATEMENT
1117
+ #
1118
+ def process_ensure(exp)
1119
+ raise if @want_expression
1120
+ try_body = process(exp.shift)
1121
+ ensure_body = without_result { process(exp.shift) }
1122
+
1123
+ "try{#{try_body}}finally{#{ensure_body}}"
1124
+ end
1125
+
1126
+ #
1127
+ # STATEMENT
1128
+ #
1129
+ def process_return(exp)
1130
+ return_value =
1131
+ if param = exp.shift
1132
+ want_expression do
1133
+ without_result do
1134
+ process(param)
1135
+ end
1136
+ end
1137
+ else
1138
+ @model.encode_nil
1139
+ end
1140
+ if @block_whileloop_stack.last == :iter
1141
+ # return from within an iterator
1142
+ throw_iter_jump(return_value, unique_method_scope())
1143
+ elsif @want_expression
1144
+ throw_iter_jump(return_value)
1145
+ else
1146
+ "return #{return_value}"
1147
+ end
1148
+ end
1149
+
1150
+ #
1151
+ # STATEMENT
1152
+ #
1153
+ def process_while(exp)
1154
+ raise if @want_expression
1155
+
1156
+ cond = exp.shift
1157
+ block = exp.shift
1158
+ flag = exp.shift
1159
+ raise unless flag == true # FIXME: document
1160
+
1161
+ str = without_result do
1162
+ c = conditionalize(cond)
1163
+ @block_whileloop_stack.push(:while)
1164
+ b = process(block)
1165
+ @block_whileloop_stack.pop || raise
1166
+
1167
+ "while(#{c}){#{b}}"
1168
+ end
1169
+
1170
+ if @want_result
1171
+ str << sep() + resultify(@model.encode_nil) + sep()
1172
+ end
1173
+
1174
+ return str
1175
+ end
1176
+
1177
+ #
1178
+ # STATEMENT
1179
+ #
1180
+ def process_break(exp)
1181
+ case @block_whileloop_stack.last
1182
+ when :while
1183
+ raise "break with arguments inside while not yet supported" unless exp.empty?
1184
+ raise if @want_expression
1185
+ "break"
1186
+ when :iter
1187
+ return_value =
1188
+ if param = exp.shift
1189
+ without_result do
1190
+ process(param)
1191
+ end
1192
+ else
1193
+ @model.encode_nil
1194
+ end
1195
+ throw_iter_jump(return_value)
1196
+ when nil
1197
+ raise("break not in loop/block scope")
1198
+ else
1199
+ raise "FATAL"
1200
+ end
1201
+ end
1202
+
1203
+ def throw_iter_jump(return_value, scope=nil)
1204
+ @iterators_used = true # used to produce catch code in this method
1205
+ iter_jump = @model.encode_globalattr('iter_jump')
1206
+ scope ||= "null"
1207
+ th =
1208
+ if @want_expression
1209
+ @model.encode_globalattr("throw_expr")
1210
+ else
1211
+ "throw"
1212
+ end
1213
+
1214
+ "#{th}(new #{iter_jump}(#{return_value},#{scope}))"
1215
+ end
1216
+
1217
+ #
1218
+ # STATEMENT
1219
+ #
1220
+ def process_next(exp)
1221
+ case @block_whileloop_stack.last
1222
+ when :while
1223
+ raise "next with arguments inside while not yet supported" unless exp.empty?
1224
+ raise if @want_expression
1225
+ "continue"
1226
+ when :iter
1227
+ # next inside a code-block generates a "return"
1228
+ raise if @want_expression
1229
+
1230
+ return_value =
1231
+ if param = exp.shift
1232
+ want_expression do
1233
+ without_result do
1234
+ process(param)
1235
+ end
1236
+ end
1237
+ else
1238
+ @model.encode_nil
1239
+ end
1240
+
1241
+ "return #{return_value}"
1242
+ when nil
1243
+ raise("next not in loop/block scope")
1244
+ else
1245
+ raise "FATAL"
1246
+ end
1247
+ end
1248
+
1249
+ #
1250
+ # UNDEFINED
1251
+ #
1252
+ # Backtick strings: `inline javascript`
1253
+ #
1254
+ # We use them for inline Javascript.
1255
+ #
1256
+ # It's unclear whether it's a STATEMENT or EXPRESSION.
1257
+ # It depends on the Javascript.
1258
+ #
1259
+ # NOTE: You have to take care to return a value yourself
1260
+ # in case of @want_result, i.e. there is no automatic handling
1261
+ # thereof.
1262
+ #
1263
+ def process_xstr(exp)
1264
+ str = exp.shift
1265
+ @model.interpolate(str)
1266
+ end
1267
+
1268
+ #
1269
+ # EXPRESSION
1270
+ #
1271
+ # String interpolation
1272
+ #
1273
+ def process_dstr(exp)
1274
+ pre_str = exp.shift
1275
+ res = without_result do
1276
+ want_expression do
1277
+ # NOTE:
1278
+ # We use +to_s+, and the + operator to
1279
+ # build the interpolated string.
1280
+ @model.add_method_call(to_s = @model.encode_method("to_s"))
1281
+ "(" + ([pre_str.inspect] + exp.map {|e| "(" + process(e) + ").#{to_s}()"}).join(" + ") + ")"
1282
+ end
1283
+ end
1284
+ exp.clear
1285
+ resultify(res)
1286
+ end
1287
+
1288
+ #
1289
+ # EXPRESSION
1290
+ #
1291
+ # Process literals
1292
+ #
1293
+ def process_lit(exp)
1294
+ lit = exp.shift
1295
+ res = case lit
1296
+ when Fixnum, Bignum, Float
1297
+ lit.to_s
1298
+ when String
1299
+ lit.inspect
1300
+ when Symbol
1301
+ lit.to_s.inspect
1302
+ when Regexp
1303
+ lit.inspect
1304
+ when Range
1305
+ range = @model.lookup_constant('::Range')
1306
+ @model.add_method_call(m = @model.encode_method("new"))
1307
+ "#{range}.#{m}(#{@model.encode_nil},#{lit.first},#{lit.last},#{lit.exclude_end?})"
1308
+ else
1309
+ raise
1310
+ end
1311
+
1312
+ resultify(res)
1313
+ end
1314
+
1315
+ def is_numeric_literal(exp)
1316
+ type = exp[0]
1317
+ return false if exp[0] != :lit
1318
+ case exp[1]
1319
+ when Fixnum, Bignum, Float
1320
+ true
1321
+ else
1322
+ false
1323
+ end
1324
+ end
1325
+
1326
+ #
1327
+ # EXPRESSION
1328
+ #
1329
+ # Process string literal
1330
+ #
1331
+ def process_str(exp)
1332
+ str = exp.shift
1333
+ resultify(str.inspect)
1334
+ end
1335
+
1336
+ #
1337
+ # EXPRESSION
1338
+ #
1339
+ def process_true(exp)
1340
+ resultify("true")
1341
+ end
1342
+
1343
+ #
1344
+ # EXPRESSION
1345
+ #
1346
+ def process_false(exp)
1347
+ resultify("false")
1348
+ end
1349
+
1350
+ #
1351
+ # EXPRESSION
1352
+ #
1353
+ def process_nil(exp)
1354
+ resultify(@model.encode_nil)
1355
+ end
1356
+
1357
+ #
1358
+ # EXPRESSION
1359
+ #
1360
+ def process_self(exp)
1361
+ resultify(@model.encode_self)
1362
+ end
1363
+
1364
+ #
1365
+ # EXPRESSION
1366
+ #
1367
+ def process_splat(exp)
1368
+ value = exp.shift
1369
+ to_splat = @model.encode_globalattr('to_splat')
1370
+ str = without_result do
1371
+ want_expression do
1372
+ "#{to_splat}(#{ process(value) })"
1373
+ end
1374
+ end
1375
+ resultify(str)
1376
+ end
1377
+
1378
+ #
1379
+ # EXPRESSION
1380
+ #
1381
+ def process_argscat(exp)
1382
+ prefix = exp.shift
1383
+ splat = exp.shift
1384
+ raise unless prefix.first == :array
1385
+ to_splat = @model.encode_globalattr('to_splat')
1386
+ str = without_result do
1387
+ want_expression do
1388
+ "#{process(prefix)}.concat(#{to_splat}(#{ process(splat) }))"
1389
+ end
1390
+ end
1391
+ resultify(str)
1392
+ end
1393
+
1394
+ #
1395
+ # EXPRESSION
1396
+ #
1397
+ # Array literal
1398
+ #
1399
+ def process_array(exp)
1400
+ str = without_result do
1401
+ want_expression do
1402
+ "[" + exp.map{|e| process(e)}.compact.join(",") + "]"
1403
+ end
1404
+ end
1405
+ exp.clear
1406
+ resultify(str)
1407
+ end
1408
+
1409
+ #
1410
+ # EXPRESSION
1411
+ #
1412
+ # We box the hash and use our own implementation, as Javascript's
1413
+ # associative arrays are very different to Ruby hashes.
1414
+ #
1415
+ def process_hash(exp)
1416
+ kv_list = exp
1417
+ raise if kv_list.length % 2 != 0
1418
+
1419
+ args =
1420
+ without_result do
1421
+ want_expression do
1422
+ kv_list.map {|i| process(i)}.join(",")
1423
+ end
1424
+ end
1425
+
1426
+ str = @model.lookup_constant('::Hash') + "."
1427
+ if kv_list.empty?
1428
+ # empty Hash
1429
+ @model.add_method_call(m = @model.encode_method("new"))
1430
+ str << "#{m}()"
1431
+ else
1432
+ @model.add_method_call(m = @model.encode_method("new_from_key_value_list"))
1433
+ str << "#{m}(#{@model.encode_nil},#{args})"
1434
+ end
1435
+
1436
+ exp.clear
1437
+
1438
+ return str
1439
+ end
1440
+
1441
+ #
1442
+ # EXPRESSION
1443
+ #
1444
+ # Empty array.
1445
+ #
1446
+ def process_zarray(exp)
1447
+ resultify("[]")
1448
+ end
1449
+
1450
+
1451
+ #
1452
+ # EXPRESSION
1453
+ #
1454
+ # Local variable assignment
1455
+ #
1456
+ # We have to write down all local variables, because they have to be
1457
+ # declared at the top of the method (otherwise they are seen as
1458
+ # "global" variables :)
1459
+ #
1460
+ def process_lasgn(exp)
1461
+ lvar = exp.shift
1462
+ value = exp.shift
1463
+
1464
+ lvar_name = @model.encode_local_variable(lvar)
1465
+ @local_variables.add(lvar_name)
1466
+ # local variables need not to be initialized, because they
1467
+ # have to be asigned to before usage, otherwise they would
1468
+ # not be local variables but methods instead.
1469
+ @local_variables_need_no_initialization.add(lvar_name)
1470
+
1471
+ str = without_result do
1472
+ want_expression do
1473
+ "#{lvar_name}=#{process(value)}"
1474
+ end
1475
+ end
1476
+
1477
+ resultify(str)
1478
+ end
1479
+
1480
+ #
1481
+ # EXPRESSION
1482
+ #
1483
+ # Local variable lookup
1484
+ #
1485
+ def process_lvar(exp)
1486
+ lvar = exp.shift
1487
+
1488
+ lvar_name = @model.encode_local_variable(lvar)
1489
+ raise "variable not available" unless @local_variables.include?(lvar_name)
1490
+
1491
+ resultify("#{lvar_name}")
1492
+ end
1493
+
1494
+ #
1495
+ # EXPRESSION
1496
+ #
1497
+ # Regular expression lookup $1 .. $9
1498
+ #
1499
+ def process_nth_ref(exp)
1500
+ n = exp.shift
1501
+ raise if n < 1 or n > 9
1502
+ resultify("(RegExp.$#{n} || #{@model.encode_nil})")
1503
+ end
1504
+
1505
+ #
1506
+ # EXPRESSION
1507
+ #
1508
+ # Global variable lookup
1509
+ #
1510
+ def process_gvar(exp)
1511
+ gvar = exp.shift
1512
+
1513
+ res =
1514
+ case gvar.to_s
1515
+ when "$!"
1516
+ # this is a special variable which holds the current exception
1517
+ exception_name()
1518
+ else
1519
+ gvar_name = @model.encode_global_variable(gvar)
1520
+ "(typeof(#{gvar_name})=='undefined'?#{@model.encode_nil}:#{gvar_name})"
1521
+ end
1522
+
1523
+ resultify(res)
1524
+ end
1525
+
1526
+ #
1527
+ # EXPRESSION
1528
+ #
1529
+ # Global variable assignment
1530
+ #
1531
+ def process_gasgn(exp)
1532
+ gvar = exp.shift
1533
+ value = exp.shift
1534
+
1535
+ gvar_name = @model.encode_global_variable(gvar)
1536
+
1537
+ str = without_result do
1538
+ want_expression do
1539
+ "#{gvar_name}=#{process(value)}"
1540
+ end
1541
+ end
1542
+
1543
+ resultify(str)
1544
+ end
1545
+
1546
+ #
1547
+ # EXPRESSION
1548
+ #
1549
+ # Dynamic variable lookup
1550
+ #
1551
+ def process_dvar(exp)
1552
+ dvar = exp.shift
1553
+ dvar_name = @model.encode_local_variable(dvar)
1554
+ raise "dynamic variable not available" unless @current_iter_dvars.include?(dvar_name)
1555
+
1556
+ resultify("#{dvar_name}")
1557
+ end
1558
+
1559
+ #
1560
+ # EXPRESSION
1561
+ #
1562
+ # Dynamic variable assignment
1563
+ #
1564
+ def process_dasgn(exp)
1565
+ dvar = exp.shift
1566
+ value = exp.shift
1567
+ dvar_name = @model.encode_local_variable(dvar)
1568
+ raise "dynamic variable not available" unless @current_iter_dvars.include?(dvar_name)
1569
+
1570
+ str = without_result do
1571
+ want_expression do
1572
+ "#{dvar_name}=#{process(value)}"
1573
+ end
1574
+ end
1575
+
1576
+ resultify(str)
1577
+ end
1578
+
1579
+ #
1580
+ # EXPRESSION
1581
+ #
1582
+ # Dynamic variable declaration and assignment
1583
+ #
1584
+ def process_dasgn_curr(exp)
1585
+ dvar = exp.shift
1586
+ value = exp.shift
1587
+ dvar_name = @model.encode_local_variable(dvar)
1588
+
1589
+ @current_iter_dvars.add(dvar_name)
1590
+
1591
+ str = without_result do
1592
+ want_expression do
1593
+ if value
1594
+ "#{dvar_name}=#{process(value)}"
1595
+ else
1596
+ # this should never happen, see process_iter()
1597
+ raise "no declarations possible"
1598
+ end
1599
+ end
1600
+ end
1601
+
1602
+ resultify(str)
1603
+ end
1604
+
1605
+ #
1606
+ # EXPRESSION
1607
+ #
1608
+ # Instance variable lookup
1609
+ #
1610
+ def process_ivar(exp)
1611
+ ivar = exp.shift
1612
+ ivar_name = @model.encode_instance_variable(ivar)
1613
+ @read_instance_variables.add(ivar_name)
1614
+ resultify("#{@model.encode_self}.#{ivar_name}")
1615
+ end
1616
+
1617
+ def process_svalue
1618
+ raise
1619
+ end
1620
+
1621
+ def process_to_ary(exp)
1622
+ value = exp.shift
1623
+ str = without_result do
1624
+ want_expression do
1625
+ case value.first
1626
+ when :lit
1627
+ "[" + process(value) + "]"
1628
+ when :array, :zarray
1629
+ process(value)
1630
+ when :special_to_ary
1631
+ # this is used inside of process_iter to build the multiple
1632
+ # assignment.
1633
+ value[1]
1634
+ else
1635
+ generate_method_call_receiver_exp(value, "to_ary", nil, nil)
1636
+ end
1637
+ end
1638
+ end
1639
+ resultify(str)
1640
+ end
1641
+
1642
+ #
1643
+ # EXPRESSION
1644
+ #
1645
+ # This is used to insert a pure inline JS string into the
1646
+ # code. It is used for example in process_masgn.
1647
+ #
1648
+ # It is not part of the ParseTree returned node types!
1649
+ #
1650
+ def process_special_inline_js_value(exp)
1651
+ return exp.shift
1652
+ end
1653
+
1654
+ #
1655
+ # EXPRESSION
1656
+ #
1657
+ # Multiple assignment
1658
+ #
1659
+ # Simple case:
1660
+ #
1661
+ # a, b = 1, 2
1662
+ #
1663
+ # [:masgn,
1664
+ # [:array, [:lasgn, :a], [:lasgn, :b]],
1665
+ # [:array, [:lit, 1], [:lit, 2]]]]]]
1666
+ #
1667
+ # Case with splat argument:
1668
+ #
1669
+ # a, *b = 1, 2, 3
1670
+ #
1671
+ # [:masgn,
1672
+ # [:array, [:lasgn, :a]],
1673
+ # [:lasgn, :b],
1674
+ # [:array, [:lit, 1], [:lit, 2], [:lit, 3]]]]]]
1675
+ #
1676
+ # Another case:
1677
+ #
1678
+ # a, b = 1
1679
+ #
1680
+ # [:masgn,
1681
+ # [:array, [:lasgn, :a], [:lasgn, :b]],
1682
+ # [:to_ary, [:lit, 1]]]
1683
+ #
1684
+ # We actually implement multiple assignment using a
1685
+ # temporary array. Example:
1686
+ #
1687
+ # a, b = b, a
1688
+ #
1689
+ # leads to the following javascript code
1690
+ #
1691
+ # (_t = [b,a],
1692
+ # a = _t[0] == null ? nil : _t[0],
1693
+ # b = _t[1] == null ? nil : _t[1])
1694
+ #
1695
+ # When a splat argument is given, there's just an
1696
+ # additional assignment which takes the rest of the
1697
+ # array.
1698
+ #
1699
+ def process_masgn(exp)
1700
+ lhs = exp.shift
1701
+ rhs = exp.pop
1702
+ splat = exp.shift
1703
+
1704
+ raise unless lhs.first == :array
1705
+ raise unless rhs.first == :array or rhs.first == :to_ary
1706
+
1707
+ want_expression do
1708
+ with_temporary_variable do |tmp|
1709
+ assgn = []
1710
+ without_result do
1711
+ assgn << "#{tmp}=#{process(rhs)}"
1712
+
1713
+ # lhs[0] == :array -> skip it
1714
+ lhs[1..-1].each_with_index do |assignment, i| # for example where assignment == [:lasgn, :a]
1715
+ assignment << s(:special_inline_js_value, "#{tmp}[#{i}]==null?#{@model.encode_nil}:#{tmp}[#{i}]")
1716
+ assgn << process(assignment)
1717
+ end
1718
+
1719
+ if splat
1720
+ # splat is for example [:lasgn, :a]
1721
+ splat << s(:special_inline_js_value, "#{tmp}.slice(#{lhs.size-1})")
1722
+ assgn << process(splat)
1723
+ end
1724
+ end
1725
+
1726
+ # return value of the expression is the array
1727
+ assgn << resultify("#{tmp}")
1728
+
1729
+ "(" + assgn.join(",") + ")"
1730
+ end
1731
+ end
1732
+ end
1733
+
1734
+ #
1735
+ # EXPRESSION
1736
+ #
1737
+ # Instance variable assignment
1738
+ #
1739
+ def process_iasgn(exp)
1740
+ ivar = exp.shift
1741
+ value = exp.shift
1742
+ ivar_name = @model.encode_instance_variable(ivar)
1743
+
1744
+ str = without_result do
1745
+ want_expression do
1746
+ "#{@model.encode_self}.#{ivar_name}=#{process(value)}"
1747
+ end
1748
+ end
1749
+
1750
+ resultify(str)
1751
+ end
1752
+
1753
+ #
1754
+ # EXPRESSION (doesn't generate any code directly!)
1755
+ #
1756
+ # Iterator block.
1757
+ #
1758
+ # We process as follows:
1759
+ #
1760
+ # 1. Generate code for the arguments of the block (multiple
1761
+ # assignment etc.)
1762
+ #
1763
+ # 2. Generate code for the code block.
1764
+ #
1765
+ # 3. In 1 and 2 we also write down all dynamic variable assignments.
1766
+ # ("dasgn_curr" which introduces a new scope)
1767
+ #
1768
+ # 4. Generate code for variable declarations.
1769
+ #
1770
+ # TODO: assign arity to function object!
1771
+ #
1772
+ def process_iter(exp)
1773
+ call = exp.shift # the method call to which the iterator is passed
1774
+ params = exp.shift # arguments of the block
1775
+ code = exp.shift # the code block
1776
+
1777
+ old_iter_dvars = @current_iter_dvars
1778
+ @current_iter_dvars = Set.new
1779
+ @block_whileloop_stack.push(:iter)
1780
+
1781
+ # Get an argument name for the iterator function signature.
1782
+ arg_name = @model.encode_fresh_local_variable()
1783
+
1784
+ fun_str = ""
1785
+ asgn_str = ""
1786
+
1787
+ if params.nil?
1788
+ # Case 1: {}: Any number of arguments may be passed
1789
+ arity = -1
1790
+ fun_str << "function(){"
1791
+ elsif params == :zero_arguments
1792
+ # Case 2: {||}: Zero arguments
1793
+ arity = 0
1794
+ fun_str << "function(){"
1795
+ elsif params.first == :masgn
1796
+ # Case 3: {|i,j|}: Multiple arguments (multiple assignment)
1797
+ arity = nil # |arity| >= 2
1798
+ fun_str << "function(#{arg_name}){"
1799
+
1800
+ # TODO: remove masgn_iter and instead put the corresponding logic
1801
+ # into yield!
1802
+ masgn_iter = @model.encode_globalattr("masgn_iter")
1803
+ params << [:to_ary, [:special_to_ary, "#{masgn_iter}(#{arg_name})"]]
1804
+ want_expression(false) do
1805
+ without_result do
1806
+ asgn_str << process(params)
1807
+ end
1808
+ end
1809
+ else
1810
+ # Case 4: {|i|}: Single argument
1811
+ # TODO: warning if not exactly one argument is passed
1812
+ arity = 1
1813
+ fun_str << "function(#{arg_name}){"
1814
+
1815
+ # we convert a single argument into a multiple assignment clause
1816
+ # with one argument.
1817
+
1818
+ sasgn_iter = @model.encode_globalattr("sasgn_iter")
1819
+ params << [:special_inline_js_value, "#{arg_name}==null?#{@model.encode_nil}:#{arg_name}"]
1820
+
1821
+ want_expression(false) do
1822
+ without_result do
1823
+ asgn_str << process(params)
1824
+ end
1825
+ end
1826
+ end
1827
+
1828
+ old_result_name = @result_name
1829
+ @result_name = nil
1830
+
1831
+ block_str = ""
1832
+ want_expression(false) do
1833
+ want_result do
1834
+ block_str << process(code) if code
1835
+ end
1836
+ end
1837
+
1838
+ # generate code for the variable declarations
1839
+ var_str = ""
1840
+ unless @current_iter_dvars.empty?
1841
+ var_str << "var "
1842
+ var_str << @current_iter_dvars.to_a.join(",")
1843
+ var_str << sep()
1844
+ end
1845
+
1846
+ # declare and initialize the return value
1847
+ if @result_name
1848
+ var_str << "var #{@result_name}=#{@model.encode_nil}#{sep()}"
1849
+ end
1850
+
1851
+ # NOTE: we don't need to initialize any dvar to nil, as
1852
+ # they can't be used before being assigned to (because
1853
+ # otherwise they are vcall's and not dynamic variables).
1854
+
1855
+ str = fun_str + var_str + asgn_str + sep() + block_str
1856
+ str << "#{sep()}return #{@result_name}" if @result_name
1857
+ str << "}"
1858
+
1859
+ put_iter(str)
1860
+
1861
+ @result_name = old_result_name
1862
+ @current_iter_dvars = old_iter_dvars
1863
+ @block_whileloop_stack.pop || raise
1864
+
1865
+ return process(call)
1866
+ end
1867
+
1868
+ #
1869
+ # a =~ /regexp/
1870
+ #
1871
+ # is converted to
1872
+ #
1873
+ # [:match3, [:lit, /regexp/], [:lvar, :a]]
1874
+ #
1875
+ # We just convert it to:
1876
+ #
1877
+ # [:call, [:lvar, :a], :=~, [:array, [:lit, /regexp/]]]
1878
+ #
1879
+ def process_match3(exp)
1880
+ right = exp.shift
1881
+ left = exp.shift
1882
+ return process(s(:call, left, :=~, s(:array, right)))
1883
+ end
1884
+
1885
+ #
1886
+ # if /regexp/ =~ a
1887
+ #
1888
+ # is converted to
1889
+ #
1890
+ # [:match2, [:lit, /regexp/], [:lvar, :a]]
1891
+ #
1892
+ # We just convert it to:
1893
+ #
1894
+ # [:call, [:lit, /regexp/], :=~, [:array, [:lvar, :a]]]
1895
+ #
1896
+ def process_match2(exp)
1897
+ left = exp.shift
1898
+ right = exp.shift
1899
+ return process(s(:call, left, :=~, s(:array, right)))
1900
+ end
1901
+
1902
+ #######################################################################
1903
+
1904
+ private
1905
+
1906
+ #######################################################################
1907
+
1908
+ def without_result(&block)
1909
+ want_result(false, &block)
1910
+ end
1911
+
1912
+ def want_result(wish=true)
1913
+ old_want_result = @want_result
1914
+ begin
1915
+ @want_result = wish
1916
+ return yield
1917
+ ensure
1918
+ @want_result = old_want_result
1919
+ end
1920
+ end
1921
+
1922
+ def resultify(str)
1923
+ if @want_result
1924
+ # FIXME
1925
+ result_name() + "=" + str
1926
+ else
1927
+ str
1928
+ end
1929
+ end
1930
+
1931
+ def result_name
1932
+ @result_name ||= @model.encode_fresh_local_variable()
1933
+ #@local_variables.add(@result_name)
1934
+ @result_name
1935
+ end
1936
+
1937
+ #
1938
+ # Generate a name for @block_name (which is the argument for
1939
+ # the block in the Javascript function signature).
1940
+ #
1941
+ def block_name
1942
+ @block_name ||= @model.encode_fresh_local_variable()
1943
+ @argument_variables.add(@block_name)
1944
+ @iterators_used = true
1945
+ # FIXME: not true. passing an interator &block does not
1946
+ # mean that iterators are used!
1947
+ @block_name
1948
+ end
1949
+
1950
+ def arguments_name()
1951
+ @arguments_name ||= @model.encode_fresh_local_variable()
1952
+ end
1953
+
1954
+ def exception_name()
1955
+ @exception_name ||= @model.encode_fresh_local_variable()
1956
+ end
1957
+
1958
+ def conditionalize(exp, negate=false)
1959
+ # Optimize literals
1960
+ case exp.first
1961
+ when :true, :lit, :self, :str # evaluate to true
1962
+ return (negate ? "false" : "true")
1963
+ when :false, :nil # evaluate to false
1964
+ return (negate ? "true" : "false")
1965
+ end
1966
+
1967
+ want_expression do
1968
+ with_temporary_variable do |tmp|
1969
+ @local_variables_need_no_initialization.add(tmp)
1970
+ if negate
1971
+ "(#{tmp}=#{process(exp)},#{tmp}===false||#{tmp}===nil)"
1972
+ else
1973
+ "(#{tmp}=#{process(exp)},#{tmp}!==false&&#{tmp}!==nil)"
1974
+ end
1975
+ end
1976
+ end
1977
+ end
1978
+
1979
+ def with_temporary_variable
1980
+ var = get_temporary_variable()
1981
+ begin
1982
+ return (yield var)
1983
+ ensure
1984
+ put_temporary_variable(var)
1985
+ end
1986
+ end
1987
+
1988
+ def get_temporary_variable
1989
+ tmp = @temporary_variables_pool.shift || @model.encode_fresh_local_variable
1990
+ @local_variables.add(tmp)
1991
+ return tmp
1992
+ end
1993
+
1994
+ def put_temporary_variable(tmp)
1995
+ @temporary_variables_pool.unshift(tmp)
1996
+ end
1997
+
1998
+ def get_iter
1999
+ res = @iter
2000
+ @iter = nil
2001
+ res
2002
+ end
2003
+
2004
+ def put_iter(iter)
2005
+ @iter = iter
2006
+ end
2007
+ end