superchris-rubyjs 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/README +131 -0
  2. data/Rakefile +65 -0
  3. data/bin/rubyjs +144 -0
  4. data/rubyjs.gemspec +112 -0
  5. data/src/rubyjs.rb +3 -0
  6. data/src/rubyjs/code_generator.rb +474 -0
  7. data/src/rubyjs/compiler.rb +2061 -0
  8. data/src/rubyjs/debug_name_generator.rb +95 -0
  9. data/src/rubyjs/encoder.rb +171 -0
  10. data/src/rubyjs/eval_into.rb +59 -0
  11. data/src/rubyjs/lib/core.rb +1016 -0
  12. data/src/rubyjs/lib/dom_element.rb +66 -0
  13. data/src/rubyjs/lib/json.rb +101 -0
  14. data/src/rubyjs/lib/microunit.rb +188 -0
  15. data/src/rubyjs/model.rb +293 -0
  16. data/src/rubyjs/name_generator.rb +71 -0
  17. data/src/rwt/AbsolutePanel.rb +161 -0
  18. data/src/rwt/DOM.Konqueror.rb +89 -0
  19. data/src/rwt/DOM.Opera.rb +65 -0
  20. data/src/rwt/DOM.rb +1044 -0
  21. data/src/rwt/Event.Opera.rb +35 -0
  22. data/src/rwt/Event.rb +429 -0
  23. data/src/rwt/HTTPRequest.IE6.rb +5 -0
  24. data/src/rwt/HTTPRequest.rb +74 -0
  25. data/src/rwt/Label.rb +164 -0
  26. data/src/rwt/Panel.rb +90 -0
  27. data/src/rwt/RootPanel.rb +16 -0
  28. data/src/rwt/UIObject.rb +495 -0
  29. data/src/rwt/Widget.rb +193 -0
  30. data/src/rwt/ported-from/AbsolutePanel.java +158 -0
  31. data/src/rwt/ported-from/DOM.java +571 -0
  32. data/src/rwt/ported-from/DOMImpl.java +426 -0
  33. data/src/rwt/ported-from/DOMImplOpera.java +82 -0
  34. data/src/rwt/ported-from/DOMImplStandard.java +234 -0
  35. data/src/rwt/ported-from/HTTPRequest.java +81 -0
  36. data/src/rwt/ported-from/HTTPRequestImpl.java +103 -0
  37. data/src/rwt/ported-from/Label.java +163 -0
  38. data/src/rwt/ported-from/Panel.java +99 -0
  39. data/src/rwt/ported-from/UIObject.java +614 -0
  40. data/src/rwt/ported-from/Widget.java +221 -0
  41. data/test/benchmark/bm_vm1_block.rb +15 -0
  42. data/test/benchmark/bm_vm1_const.rb +13 -0
  43. data/test/benchmark/bm_vm1_ensure.rb +15 -0
  44. data/test/benchmark/common.rb +5 -0
  45. data/test/benchmark/params.yaml +7 -0
  46. data/test/common.Browser.rb +13 -0
  47. data/test/common.rb +8 -0
  48. data/test/gen_browser_test_suite.rb +129 -0
  49. data/test/gen_test_suite.rb +41 -0
  50. data/test/run_benchs.rb +58 -0
  51. data/test/run_tests.rb +22 -0
  52. data/test/test_args.rb +24 -0
  53. data/test/test_array.rb +22 -0
  54. data/test/test_case.rb +35 -0
  55. data/test/test_class.rb +55 -0
  56. data/test/test_eql.rb +9 -0
  57. data/test/test_exception.rb +61 -0
  58. data/test/test_expr.rb +12 -0
  59. data/test/test_hash.rb +29 -0
  60. data/test/test_hot_ruby.rb +146 -0
  61. data/test/test_if.rb +28 -0
  62. data/test/test_insertion_sort.rb +25 -0
  63. data/test/test_inspect.rb +10 -0
  64. data/test/test_lebewesen.rb +39 -0
  65. data/test/test_massign.rb +66 -0
  66. data/test/test_new.rb +12 -0
  67. data/test/test_range.rb +70 -0
  68. data/test/test_regexp.rb +22 -0
  69. data/test/test_send.rb +65 -0
  70. data/test/test_simple_output.rb +5 -0
  71. data/test/test_splat.rb +21 -0
  72. data/test/test_string.rb +51 -0
  73. data/test/test_test.rb +17 -0
  74. data/test/test_yield.rb +154 -0
  75. data/utils/js/Makefile +9 -0
  76. data/utils/js/RunScript.class +0 -0
  77. data/utils/js/RunScript.java +73 -0
  78. data/utils/js/js.jar +0 -0
  79. data/utils/js/run.sh +3 -0
  80. data/utils/jsc/Makefile +7 -0
  81. data/utils/jsc/README +3 -0
  82. data/utils/jsc/RunScript.c +93 -0
  83. data/utils/jsc/run.sh +15 -0
  84. data/utils/yuicompressor/README +1 -0
  85. data/utils/yuicompressor/yuicompressor-2.2.5.jar +0 -0
  86. 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