opal 0.3.19 → 0.3.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +3 -1
  2. data/Gemfile +3 -2
  3. data/README.md +304 -48
  4. data/Rakefile +1 -2
  5. data/core/alpha.rb +2 -1
  6. data/core/array.rb +92 -96
  7. data/core/basic_object.rb +1 -10
  8. data/core/boolean.rb +6 -18
  9. data/core/class.rb +9 -10
  10. data/core/comparable.rb +1 -1
  11. data/core/enumerable.rb +11 -11
  12. data/core/enumerator.rb +2 -10
  13. data/core/error.rb +16 -31
  14. data/core/hash.rb +32 -36
  15. data/core/json.rb +50 -0
  16. data/core/kernel.rb +48 -57
  17. data/core/load_order +3 -5
  18. data/core/module.rb +37 -35
  19. data/core/nil_class.rb +4 -0
  20. data/core/numeric.rb +10 -30
  21. data/core/proc.rb +1 -1
  22. data/core/range.rb +3 -4
  23. data/core/regexp.rb +21 -6
  24. data/core/runtime.js +278 -370
  25. data/core/string.rb +21 -37
  26. data/core/struct.rb +11 -3
  27. data/core/time.rb +44 -37
  28. data/lib/opal.rb +3 -3
  29. data/lib/opal/builder.rb +48 -27
  30. data/lib/opal/builder_task.rb +3 -20
  31. data/lib/opal/grammar.rb +18 -13
  32. data/lib/opal/grammar.y +7 -4
  33. data/lib/opal/parser.rb +290 -199
  34. data/lib/opal/scope.rb +187 -176
  35. data/lib/opal/version.rb +1 -1
  36. data/test/core/kernel/define_singleton_method_spec.rb +21 -0
  37. data/test/core/time/at_spec.rb +7 -0
  38. data/test/core/time/day_spec.rb +5 -0
  39. data/test/core/time/friday_spec.rb +9 -0
  40. data/test/core/time/hour_spec.rb +5 -0
  41. data/test/core/time/min_spec.rb +5 -0
  42. data/test/core/time/monday_spec.rb +9 -0
  43. data/test/core/time/month_spec.rb +5 -0
  44. data/test/core/time/now_spec.rb +5 -0
  45. data/test/core/time/saturday_spec.rb +9 -0
  46. data/test/index.html +2 -1
  47. data/test/language/singleton_class_spec.rb +0 -16
  48. data/test/opal/array/to_json_spec.rb +7 -0
  49. data/test/opal/boolean/singleton_class_spec.rb +9 -0
  50. data/test/opal/boolean/to_json_spec.rb +9 -0
  51. data/test/opal/hash/to_json_spec.rb +9 -0
  52. data/test/opal/json/parse_spec.rb +31 -0
  53. data/test/opal/kernel/to_json_spec.rb +5 -0
  54. data/test/opal/nil/to_json_spec.rb +5 -0
  55. data/test/opal/numeric/to_json_spec.rb +6 -0
  56. data/test/opal/runtime/call_spec.rb +16 -0
  57. data/test/opal/runtime/defined_spec.rb +11 -0
  58. data/test/opal/runtime/super_spec.rb +16 -0
  59. data/test/opal/string/to_json_spec.rb +6 -0
  60. data/test/spec_helper.rb +1 -3
  61. metadata +48 -15
  62. data/core/dir.rb +0 -89
  63. data/core/file.rb +0 -85
  64. data/core/match_data.rb +0 -35
  65. data/core/rational.rb +0 -16
  66. data/test/core/file/expand_path_spec.rb +0 -20
@@ -4,8 +4,7 @@ module Opal
4
4
  class BuilderTask
5
5
  include Rake::DSL if defined? Rake::DSL
6
6
 
7
- attr_accessor :name, :build_dir, :specs_dir, :files, :dependencies,
8
- :main, :specs_main
7
+ attr_accessor :name, :build_dir, :specs_dir, :files, :dependencies
9
8
 
10
9
  def initialize(namespace = nil)
11
10
  @project_dir = Dir.getwd
@@ -16,7 +15,6 @@ module Opal
16
15
  @files = Dir['lib/**/*.{rb,js}']
17
16
  @dependencies = []
18
17
  @debug_mode = false
19
- @spec_main = "spec/spec_helper"
20
18
 
21
19
  yield self if block_given?
22
20
 
@@ -29,9 +27,7 @@ module Opal
29
27
  :build_dir => @build_dir,
30
28
  :specs_dir => @specs_dir,
31
29
  :files => @files,
32
- :dependencies => @dependencies,
33
- :main => get_main,
34
- :specs_main => @specs_main
30
+ :dependencies => @dependencies
35
31
  }
36
32
  end
37
33
 
@@ -51,30 +47,17 @@ module Opal
51
47
  Builder.build opts
52
48
  end
53
49
 
54
- def get_main
55
- return @main if @main
56
-
57
- unless @files.empty?
58
- f = @files.first
59
- return f.chomp(File.extname(f))
60
- end
61
-
62
- nil
63
- end
64
-
65
50
  def define_tasks
66
51
  define_task :build, "Build Opal Project" do
67
52
  name = @debug_mode ? "#@name.debug.js" : "#@name.js"
68
53
  build_files :files => @files,
69
- :out => File.join(@build_dir, "#@name.js"),
70
- :main => get_main
54
+ :out => File.join(@build_dir, "#@name.js")
71
55
  end
72
56
 
73
57
  define_task :spec, "Build Specs" do
74
58
  name = @debug_mode ? "#@name.specs.debug.js" : "#@name.specs.js"
75
59
  build_files :files => @specs_dir,
76
60
  :out => File.join(@build_dir, name),
77
- :main => @specs_main,
78
61
  :debug => @debug_mode
79
62
  end
80
63
 
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # DO NOT MODIFY!!!!
3
- # This file is automatically generated by Racc 1.4.7
3
+ # This file is automatically generated by Racc 1.4.8
4
4
  # from Racc grammer file "".
5
5
  #
6
6
 
@@ -710,7 +710,7 @@ clist = [
710
710
  ',118,136,134,133,129,130,125,123,116,142,117,459,403,141,,460,,,,,,',
711
711
  ',137,138,,135,119,120,121,143,124,126,,,122,,,,,139,140,127,128,,,,',
712
712
  ',257,,,,,,,132,131,,118,136,134,133,129,130,125,123,116,142,117,,,141' ]
713
- racc_action_table = arr = Array.new(21316, nil)
713
+ racc_action_table = arr = ::Array.new(21316, nil)
714
714
  idx = 0
715
715
  clist.each do |str|
716
716
  str.split(',', -1).each do |i|
@@ -1480,7 +1480,7 @@ clist = [
1480
1480
  '416,416,416,416,416,416,416,416,416,665,665,416,,665,,,,,,,,665,665',
1481
1481
  ',665,665,665,665,665,665,665,,,665,,,,,665,665,665,665,,,,,,665,,,,',
1482
1482
  ',,665,665,,665,665,665,665,665,665,665,665,665,665,665,,,665' ]
1483
- racc_action_check = arr = Array.new(21316, nil)
1483
+ racc_action_check = arr = ::Array.new(21316, nil)
1484
1484
  idx = 0
1485
1485
  clist.each do |str|
1486
1486
  str.split(',', -1).each do |i|
@@ -1730,7 +1730,7 @@ clist = [
1730
1730
  ',,,788,,,,792,815,,,777,,,,,,,,,,,,,,,,,,,,,,239,,,,,,,,,635,,,,,,,',
1731
1731
  ',,,239,,,,,,,,,635,,,,,,,,,,,,,,239,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
1732
1732
  ',,725,,,,,,,,,,,297,,,,,,,,,,,,,,,,,,725' ]
1733
- racc_goto_table = arr = Array.new(2089, nil)
1733
+ racc_goto_table = arr = ::Array.new(2089, nil)
1734
1734
  idx = 0
1735
1735
  clist.each do |str|
1736
1736
  str.split(',', -1).each do |i|
@@ -1794,7 +1794,7 @@ clist = [
1794
1794
  ',,,,,,,,,,9,9,,,,,26,,,,9,,,,9,23,,,26,,,,,,,,,,,,,,,,,,,,,,26,,,,,',
1795
1795
  ',,,23,,,,,,,,,,,26,,,,,,,,,23,,,,,,,,,,,,,,26,,,,,,,,,,,,,,,,,,,,,,',
1796
1796
  ',,,,,,,,,,,,9,,,,,,,,,,,9,,,,,,,,,,,,,,,,,,9' ]
1797
- racc_goto_check = arr = Array.new(2089, nil)
1797
+ racc_goto_check = arr = ::Array.new(2089, nil)
1798
1798
  idx = 0
1799
1799
  clist.each do |str|
1800
1800
  str.split(',', -1).each do |i|
@@ -2183,8 +2183,8 @@ racc_reduce_table = [
2183
2183
  2, 207, :_reduce_344,
2184
2184
  4, 207, :_reduce_345,
2185
2185
  3, 207, :_reduce_346,
2186
- 4, 207, :_reduce_none,
2187
- 3, 207, :_reduce_none,
2186
+ 4, 207, :_reduce_347,
2187
+ 3, 207, :_reduce_348,
2188
2188
  2, 207, :_reduce_349,
2189
2189
  1, 207, :_reduce_350,
2190
2190
  0, 248, :_reduce_351,
@@ -3635,10 +3635,7 @@ def _reduce_214(val, _values, result)
3635
3635
  end
3636
3636
 
3637
3637
  def _reduce_215(val, _values, result)
3638
- result = s(:array, val[0])
3639
- #result.line = val[0].line
3640
- # FIXME:
3641
- result = s(:array)
3638
+ result = s(:array, s(:hash, *val[0]))
3642
3639
 
3643
3640
  result
3644
3641
  end
@@ -4321,9 +4318,17 @@ def _reduce_346(val, _values, result)
4321
4318
  result
4322
4319
  end
4323
4320
 
4324
- # reduce 347 omitted
4321
+ def _reduce_347(val, _values, result)
4322
+ result = new_call val[0], val[2].intern, val[3]
4323
+
4324
+ result
4325
+ end
4325
4326
 
4326
- # reduce 348 omitted
4327
+ def _reduce_348(val, _values, result)
4328
+ result = new_call val[0], val[2].intern, s(:arglist)
4329
+
4330
+ result
4331
+ end
4327
4332
 
4328
4333
  def _reduce_349(val, _values, result)
4329
4334
  result = new_super val[1]
@@ -590,10 +590,7 @@ aref_args:
590
590
  }
591
591
  | assocs trailer
592
592
  {
593
- result = s(:array, val[0])
594
- #result.line = val[0].line
595
- # FIXME:
596
- result = s(:array)
593
+ result = s(:array, s(:hash, *val[0]))
597
594
  }
598
595
 
599
596
  paren_args:
@@ -1100,7 +1097,13 @@ method_call:
1100
1097
  result = new_call val[0], :call, val[2]
1101
1098
  }
1102
1099
  | primary_value '::' operation2 paren_args
1100
+ {
1101
+ result = new_call val[0], val[2].intern, val[3]
1102
+ }
1103
1103
  | primary_value '::' operation3
1104
+ {
1105
+ result = new_call val[0], val[2].intern, s(:arglist)
1106
+ }
1104
1107
  | SUPER paren_args
1105
1108
  {
1106
1109
  result = new_super val[1]
@@ -55,50 +55,20 @@ module Opal
55
55
 
56
56
  STATEMENTS = [:xstr, :dxstr]
57
57
 
58
- DEBUG_CODE = <<-CODE
59
- var __const_get = function(const_table, id) {
60
- if (const_table && const_table[id]) {
61
- return const_table[id];
62
- }
63
-
64
- throw new Error('uninitialized constant ' + id);
65
- };
66
-
67
- var __send = function(recv, mid, jsid, block) {
68
- var args = Array.prototype.slice.call(arguments, 4);
69
-
70
- if (recv == null) {
71
- throw new Error("cannot send '" + mid + "' to null");
72
- }
73
-
74
- var func = recv[jsid];
75
-
76
- if (!func) {
77
- throw new Error(recv + " does not respond to '" + mid + "'");
78
- }
79
-
80
- func._p = block;
81
-
82
- return func.apply(recv, args);
83
- };
84
-
85
- var __send_splat = function(recv, mid, jsid, block, splat) {
86
- return __send.apply(null, [recv, mid, jsid, block].concat(splat));
87
- };
88
- CODE
89
-
90
58
  attr_reader :grammar
91
59
 
60
+ attr_reader :requires
61
+
92
62
  def self.parse(str)
93
63
  self.new.parse str
94
64
  end
95
65
 
96
66
  def initialize(opts = {})
97
- @debug = opts[:debug] or false
98
67
  end
99
68
 
100
69
  def parse(source, file = '(file)')
101
70
  @file = file
71
+ @requires = []
102
72
  @helpers = {
103
73
  :breaker => true,
104
74
  :slice => true
@@ -109,8 +79,12 @@ module Opal
109
79
  top @grammar.parse(source, file)
110
80
  end
111
81
 
112
- def raise(msg)
113
- super "#{msg} :#{@file}:#{@line}"
82
+ def warn(msg)
83
+ puts "#{msg} :#{@file}:#{@line}"
84
+ end
85
+
86
+ def error(msg)
87
+ raise "#{msg} :#{@file}:#{@line}"
114
88
  end
115
89
 
116
90
  def parser_indent
@@ -152,8 +126,10 @@ module Opal
152
126
  }
153
127
 
154
128
  vars << "__opal = Opal"
129
+ vars << "self = __opal.top"
155
130
  vars << "__scope = __opal"
156
131
  vars << "nil = __opal.nil"
132
+ vars << "def = #{current_self}._klass.prototype" if @scope.defines_defn
157
133
  vars.concat @helpers.keys.map { |h| "__#{h} = __opal.#{h}" }
158
134
 
159
135
  code = "var #{vars.join ', '};\n" + @scope.to_vars + "\n" + code
@@ -162,10 +138,9 @@ module Opal
162
138
  pre = "function() {\n"
163
139
  post = ""
164
140
 
165
- pre += DEBUG_CODE if @debug
166
141
  uniques = []
167
142
 
168
- @unique.times { |i| uniques << "TMP_#{i+1}" }
143
+ # @unique.times { |i| uniques << "TMP_#{i+1}" }
169
144
 
170
145
  unless uniques.empty?
171
146
  post += ";var #{uniques.join ', '};"
@@ -189,8 +164,17 @@ module Opal
189
164
  def indent(&block)
190
165
  indent = @indent
191
166
  @indent += INDENT
167
+ @space = "\n#@indent"
192
168
  res = yield
193
169
  @indent = indent
170
+ @space = "\n#@indent"
171
+ res
172
+ end
173
+
174
+ def with_temp(&block)
175
+ tmp = @scope.new_temp
176
+ res = yield tmp
177
+ @scope.queue_temp tmp
194
178
  res
195
179
  end
196
180
 
@@ -220,6 +204,8 @@ module Opal
220
204
  meth = "process_#{type}"
221
205
  raise "Unsupported sexp: #{type}" unless respond_to? meth
222
206
 
207
+ @line = sexp.line
208
+
223
209
  __send__ meth, sexp, level
224
210
  end
225
211
 
@@ -229,6 +215,9 @@ module Opal
229
215
  case sexp.first
230
216
  when :break, :next
231
217
  sexp
218
+ when :yield
219
+ sexp[0] = :returnable_yield
220
+ sexp
232
221
  when :scope
233
222
  sexp
234
223
  when :block
@@ -281,7 +270,7 @@ module Opal
281
270
  result << (expr ? "#{code};" : code)
282
271
  end
283
272
 
284
- result.join "\n#@indent"
273
+ result.join(@scope.class_scope? ? "\n\n#@indent" : "\n#@indent")
285
274
  end
286
275
 
287
276
  def process_scope(sexp, level)
@@ -310,16 +299,15 @@ module Opal
310
299
  meth, recv, arg = sexp
311
300
  mid = mid_to_jsid meth.to_s
312
301
 
313
- a = @scope.new_temp
314
- b = @scope.new_temp
315
- l = process recv, :expr
316
- r = process arg, :expr
317
-
318
- @scope.queue_temp a
319
- @scope.queue_temp b
302
+ with_temp do |a|
303
+ with_temp do |b|
304
+ l = process recv, :expr
305
+ r = process arg, :expr
320
306
 
321
- "(%s = %s, %s = %s, typeof(%s) === 'number' ? %s %s %s : %s.%s(%s))" %
322
- [a, l, b, r, a, a, meth.to_s, b, a, mid, b]
307
+ "(%s = %s, %s = %s, typeof(%s) === 'number' ? %s %s %s : %s.%s(%s))" %
308
+ [a, l, b, r, a, a, meth.to_s, b, a, mid, b]
309
+ end
310
+ end
323
311
  end
324
312
 
325
313
  def js_block_given(sexp, level)
@@ -327,6 +315,13 @@ module Opal
327
315
  "(#{@scope.block_name} !== nil)"
328
316
  end
329
317
 
318
+ def handle_block_given(sexp, reverse = false)
319
+ @scope.uses_block!
320
+ name = @scope.block_name
321
+
322
+ reverse ? "#{ name } === nil" : "#{ name } !== nil"
323
+ end
324
+
330
325
  # s(:lit, 1)
331
326
  # s(:lit, :foo)
332
327
  def process_lit(sexp, level)
@@ -398,8 +393,10 @@ module Opal
398
393
  "false".inspect
399
394
  when :call
400
395
  mid = mid_to_jsid part[2].to_s
401
- recv = part[1] ? process(part[1], :expr) : 'this'
396
+ recv = part[1] ? process(part[1], :expr) : current_self
402
397
  "(#{recv}.#{mid} ? 'method' : nil)"
398
+ when :xstr
399
+ "(typeof(#{process part, :expression}) !== 'undefined')"
403
400
  else
404
401
  raise "bad defined? part: #{part[0]}"
405
402
  end
@@ -457,29 +454,38 @@ module Opal
457
454
  end
458
455
 
459
456
  if block_arg
460
- @scope.add_arg block_arg
461
- code += "var #{block_arg} = _$ || nil, $context = #{block_arg}.$S;"
457
+ @scope.block_name = block_arg
458
+ @scope.add_temp block_arg
459
+ @scope.add_temp '__context'
460
+ scope_name = @scope.identify!
461
+ # @scope.add_arg block_arg
462
+ # code += "var #{block_arg} = _$ || nil, $context = #{block_arg}.$S;"
463
+ blk = "\n%s%s = %s._p || nil, __context = %s._s, %s.p = null;\n%s" %
464
+ [@indent, block_arg, scope_name, block_arg, scope_name, @indent]
465
+
466
+ code = blk + code
462
467
  end
463
468
 
464
469
  code += "\n#@indent" + process(body, :stmt)
465
470
 
471
+ if @scope.defines_defn
472
+ @scope.add_temp 'def = (this._isObject ? this._klass.prototype : this.prototype)'
473
+ end
474
+
466
475
  code = "\n#@indent#{@scope.to_vars}\n#@indent#{code}"
467
476
 
468
477
  scope_name = @scope.identity
469
478
  end
470
479
  end
471
480
 
472
- tmp = @scope.new_temp
473
-
474
- itercode = "function(#{params.join ', '}) {\n#{code}\n#@indent}"
475
- itercode = "#{scope_name} = #{itercode}" if scope_name
481
+ with_temp do |tmp|
482
+ itercode = "function(#{params.join ', '}) {\n#{code}\n#@indent}"
483
+ itercode = "#{scope_name} = #{itercode}" if scope_name
476
484
 
477
- call << ("(%s = %s, %s._s = this, %s)" % [tmp, itercode, tmp, tmp])
485
+ call << ("(%s = %s, %s._s = %s, %s)" % [tmp, itercode, tmp, current_self, tmp])
478
486
 
479
- res = process call, level
480
- @scope.queue_temp tmp
481
-
482
- res
487
+ process call, level
488
+ end
483
489
  end
484
490
 
485
491
  def js_block_args(sexp)
@@ -518,23 +524,33 @@ module Opal
518
524
  out = []
519
525
 
520
526
  attrs.each do |attr|
521
- ivar = attr[1].to_s
527
+ mid = attr[1]
528
+ ivar = "@#{mid}".intern
529
+ pre = @scope.proto
522
530
 
523
531
  unless meth == :attr_writer
524
- attr = mid_to_jsid ivar
525
- check = "this.#{ivar} == null ? nil : this.#{ivar}"
526
- out << "def.#{attr} = function() { return #{check}; }"
532
+ out << process(s(:defn, mid, s(:args), s(:scope, s(:ivar, ivar))), :stmt)
527
533
  end
528
534
 
529
535
  unless meth == :attr_reader
530
- attr = mid_to_jsid "#{ivar}="
531
- out << "def.#{attr} = function(val) { return this.#{ivar} = val }"
536
+ mid = "#{mid}=".intern
537
+ out << process(s(:defn, mid, s(:args, :val), s(:scope,
538
+ s(:iasgn, ivar, s(:lvar, :val)))), :stmt)
532
539
  end
533
540
  end
534
541
 
535
542
  out.join ", \n#@indent"
536
543
  end
537
544
 
545
+ def handle_alias_native(sexp)
546
+ args = sexp[2]
547
+ meth = mid_to_jsid args[1][1].to_s
548
+ func = args[2][1]
549
+
550
+ @scope.methods << meth
551
+ "%s.%s = %s.%s" % [@scope.proto, meth, @scope.proto, func]
552
+ end
553
+
538
554
  # s(:call, recv, :mid, s(:arglist))
539
555
  # s(:call, nil, :mid, s(:arglist))
540
556
  def process_call(sexp, level)
@@ -549,16 +565,19 @@ module Opal
549
565
  end
550
566
  when :block_given?
551
567
  return js_block_given(sexp, level)
568
+ when :alias_native
569
+ return handle_alias_native(sexp) if @scope.class_scope?
552
570
  when :require
553
- # path = arglist[1]
571
+ path = arglist[1]
554
572
 
555
- # if path and path[0] == :str
556
- # path_name = path[1].sub(/^opal\//, '')
557
- # @requires << path_name
558
- # return "nil"
559
- # else
560
- # raise "Opal cannot do dynamic requires"
561
- # end
573
+ if path and path[0] == :str
574
+ path_name = path[1].sub(/^opal\//, '')
575
+ @requires << path_name
576
+ return "nil"
577
+ else
578
+ warn "Opal cannot do dynamic requires"
579
+ return "nil"
580
+ end
562
581
  end
563
582
 
564
583
  splat = arglist[1..-1].any? { |a| a.first == :splat }
@@ -649,17 +668,17 @@ module Opal
649
668
  body[1] = s(:nil) unless body[1]
650
669
 
651
670
  code = nil
652
- @helpers[:klass] = @helpers[:donate] = true
671
+ @helpers[:klass] = true
653
672
 
654
673
  if Symbol === cid or String === cid
655
- base = 'this'
656
- name = cid.to_s.inspect
674
+ base = current_self
675
+ name = cid.to_s
657
676
  elsif cid[0] == :colon2
658
677
  base = process(cid[1], :expr)
659
- name = cid[2].to_s.inspect
678
+ name = cid[2].to_s
660
679
  elsif cid[0] == :colon3
661
680
  base = 'Opal.Object'
662
- name = cid[1].to_s.inspect
681
+ name = cid[1].to_s
663
682
  else
664
683
  raise "Bad receiver in class"
665
684
  end
@@ -668,13 +687,21 @@ module Opal
668
687
 
669
688
  indent do
670
689
  in_scope(:class) do
690
+ @scope.name = name
691
+ @scope.add_temp "#{ @scope.proto } = #{name}.prototype", "__scope = #{name}._scope"
671
692
  @scope.donates_methods = true
672
- code = @indent + @scope.to_vars + "\n#@indent" + process(body, :stmt)
693
+ body = process body, :stmt
694
+ code = @indent + @scope.to_vars + "\n\n#@indent" + body
673
695
  code += "\n#{@scope.to_donate_methods}"
674
696
  end
675
697
  end
676
698
 
677
- "__klass(#{base}, #{sup}, #{name}, function() {\n#{code}\n#@indent})"
699
+ spacer = "\n#{@indent}#{INDENT}"
700
+ cls = "function #{name}() {};"
701
+ boot = "#{name} = __klass(__base, __super, #{name.inspect}, #{name});"
702
+ comment = "#{spacer}// line #{ sexp.line }, #{ @file }, class #{ name }#{spacer}"
703
+
704
+ "(function(__base, __super){#{comment}#{cls}#{spacer}#{boot}\n#{code}\n#{@indent}})(#{base}, #{sup})"
678
705
  end
679
706
 
680
707
  # s(:sclass, recv, body)
@@ -683,13 +710,13 @@ module Opal
683
710
  body = sexp[1]
684
711
  code = nil
685
712
  base = process recv, :expr
686
- @helpers[:sklass] = true
687
713
 
688
714
  in_scope(:sclass) do
715
+ @scope.add_temp '__scope = this._scope'
689
716
  code = @scope.to_vars + process(body, :stmt)
690
717
  end
691
718
 
692
- "__sklass(#{base}, function() {#{code}})"
719
+ "(function(){#{ code }}).call(#{ base }.$singleton_class())"
693
720
  end
694
721
 
695
722
  # s(:module, cid, body)
@@ -697,29 +724,37 @@ module Opal
697
724
  cid = sexp[0]
698
725
  body = sexp[1]
699
726
  code = nil
700
- @helpers[:module] = @helpers[:donate] = true
727
+ @helpers[:module] = true
701
728
 
702
729
  if Symbol === cid or String === cid
703
- base = 'this'
704
- name = cid.to_s.inspect
730
+ base = current_self
731
+ name = cid.to_s
705
732
  elsif cid[0] == :colon2
706
733
  base = process(cid[1], :expr)
707
- name = cid[2].to_s.inspect
734
+ name = cid[2].to_s
708
735
  elsif cid[0] == :colon3
709
736
  base = 'Opal.Object'
710
- name = cid[1].to_s.inspect
737
+ name = cid[1].to_s
711
738
  else
712
739
  raise "Bad receiver in class"
713
740
  end
714
741
 
715
742
  indent do
716
743
  in_scope(:module) do
744
+ @scope.name = name
745
+ @scope.add_temp "#{ @scope.proto } = #{name}.prototype", "__scope = #{name}._scope"
717
746
  @scope.donates_methods = true
718
- code = @indent + @scope.to_vars + "\n#@indent" + process(body, :stmt) + "\n#@indent" + @scope.to_donate_methods
747
+ body = process body, :stmt
748
+ code = @indent + @scope.to_vars + "\n\n#@indent" + body + "\n#@indent" + @scope.to_donate_methods
719
749
  end
720
750
  end
721
751
 
722
- "__module(#{base}, #{name}, function() {\n#{code}\n#@indent})"
752
+ spacer = "\n#{@indent}#{INDENT}"
753
+ cls = "function #{name}() {};"
754
+ boot = "#{name} = __module(__base, #{name.inspect}, #{name});"
755
+ comment = "#{spacer}// line #{ sexp.line }, #{ @file }, module #{ name }#{spacer}"
756
+
757
+ "(function(__base){#{comment}#{cls}#{spacer}#{boot}\n#{code}\n#{@indent}})(#{base})"
723
758
  end
724
759
 
725
760
  def process_undef(exp, level)
@@ -730,7 +765,7 @@ module Opal
730
765
  # FIXME: maybe add this to donate(). it will be undefined, so
731
766
  # when added to includees it will actually undefine methods there
732
767
  # too.
733
- "delete def.#{jsid}"
768
+ "delete #{ @scope.proto }.#{jsid}"
734
769
  end
735
770
 
736
771
  # s(:defn, mid, s(:args), s(:scope))
@@ -751,14 +786,15 @@ module Opal
751
786
  end
752
787
 
753
788
  def js_def(recvr, mid, args, stmts, line, end_line)
754
- mid = mid_to_jsid mid.to_s
789
+ jsid = mid_to_jsid mid.to_s
755
790
 
756
791
  if recvr
757
792
  @scope.defines_defs = true
793
+ smethod = true if @scope.class_scope? && recvr.first == :self
758
794
  recv = process(recvr, :expr)
759
795
  else
760
796
  @scope.defines_defn = true
761
- recv = 'this'
797
+ recv = current_self
762
798
  end
763
799
 
764
800
  code = ''
@@ -784,66 +820,71 @@ module Opal
784
820
  end
785
821
  end
786
822
 
787
- # aritycode = arity_check(args, opt, splat) if @debug && false
788
-
789
823
  indent do
790
- in_scope(:def) do
791
- @scope.mid = mid
824
+ in_scope(:def) do
825
+ @scope.mid = mid
826
+ @scope.defs = true if recvr
792
827
 
793
- if block_name
794
- @scope.uses_block!
795
- end
828
+ if block_name
829
+ @scope.uses_block!
830
+ end
796
831
 
797
- yielder = block_name || '__yield'
798
- @scope.block_name = yielder
832
+ yielder = block_name || '__yield'
833
+ @scope.block_name = yielder
799
834
 
800
- params = process args, :expr
835
+ params = process args, :expr
801
836
 
802
- opt[1..-1].each do |o|
803
- next if o[2][2] == :undefined
804
- id = process s(:lvar, o[1]), :expr
805
- code += "if (#{id} == null) {\n#@indent#{INDENT}#{process o, :expr};\n#@indent}"
806
- end if opt
837
+ opt[1..-1].each do |o|
838
+ next if o[2][2] == :undefined
839
+ id = process s(:lvar, o[1]), :expr
840
+ code += ("if (%s == null) {\n%s%s\n%s}" %
841
+ [id, @indent + INDENT, process(o, :expre), @indent])
842
+ end if opt
807
843
 
808
- code += "#{splat} = __slice.call(arguments, #{len});" if splat
809
- code += "\n#@indent" + process(stmts, :stmt)
844
+ code += "#{splat} = __slice.call(arguments, #{len});" if splat
845
+ code += "\n#@indent" + process(stmts, :stmt)
810
846
 
811
- # Returns the identity name if identified, nil otherwise
812
- scope_name = @scope.identity
847
+ # Returns the identity name if identified, nil otherwise
848
+ scope_name = @scope.identity
813
849
 
814
- if @scope.uses_block?
815
- @scope.add_temp '__context'
816
- @scope.add_temp yielder
817
- blk = "\n#{@indent}#{yielder} = #{scope_name}._p || nil;\n#{@indent}__context = #{yielder}._s"
818
- blk += ";\n#{@indent}#{scope_name}._p = null;\n#{@indent}"
819
- code = blk + code
820
- end
850
+ if @scope.uses_block?
851
+ @scope.add_temp '__context'
852
+ @scope.add_temp yielder
821
853
 
822
- if @scope.catches_break?
823
- # code = "try {#{code}} catch (e) { if (e === __breaker) { return e.$v; }; throw e;}"
824
- end
854
+ blk = "\n%s%s = %s._p || nil, __context = %s._s, %s._p = null;\n%s" %
855
+ [@indent, yielder, scope_name, yielder, scope_name, @indent]
825
856
 
826
- code = "#@indent#{@scope.to_vars}" + code
827
- end
857
+ code = blk + code
858
+ end
859
+
860
+ code = "#@indent#{@scope.to_vars}" + code
861
+ end
828
862
  end
829
863
 
830
864
  defcode = "#{"#{scope_name} = " if scope_name}function(#{params}) {\n#{code}\n#@indent}"
831
865
 
866
+ comment = "// line #{line}, #{@file}"
867
+
868
+ if @scope.class_scope?
869
+ comment += ", #{ @scope.name }#{ recvr ? '.' : '#' }#{ mid }"
870
+ end
871
+
872
+ comment += "\n#{@indent}"
873
+
832
874
  if recvr
833
- # FIXME: need to donate()
834
- "#{recv}.$singleton_class()._proto.#{mid} = #{defcode}"
835
- elsif @scope.type == :class
836
- @scope.methods << mid# if @scope.donates_methods
837
- "def.#{mid} = #{defcode}"
838
- elsif @scope.type == :module
839
- @scope.methods << mid
840
- "def.#{mid} = #{defcode}"
875
+ if smethod
876
+ @scope.smethods << jsid
877
+ "#{ comment }#{ @scope.name }.#{jsid} = #{defcode}"
878
+ else
879
+ "#{recv}.$singleton_class().prototype.#{jsid} = #{defcode}"
880
+ end
881
+ elsif @scope.class_scope?
882
+ @scope.methods << jsid
883
+ "#{ comment }#{ @scope.proto }.#{jsid} = #{defcode}"
841
884
  elsif @scope.type == :iter
842
- # FIXME: this should also donate()
843
- "def.#{mid} = #{defcode}"
885
+ "def.#{jsid} = #{defcode}"
844
886
  else
845
- # FIXME: this should also donate()
846
- "def.#{mid} = #{defcode}"
887
+ "def.#{jsid} = #{defcode}"
847
888
  end
848
889
  end
849
890
 
@@ -878,7 +919,20 @@ module Opal
878
919
 
879
920
  # s(:self) # => this
880
921
  def process_self(sexp, level)
881
- 'this'
922
+ current_self
923
+ end
924
+
925
+ # Returns the current value for 'self'. This will be native
926
+ # 'this' for methods and blocks, and the class name for class
927
+ # and module bodies.
928
+ def current_self
929
+ if @scope.class_scope?
930
+ @scope.name
931
+ elsif @scope.top?
932
+ 'self'
933
+ else
934
+ 'this'
935
+ end
882
936
  end
883
937
 
884
938
  # s(:true) # => true
@@ -960,7 +1014,7 @@ module Opal
960
1014
  @scope.queue_temp redo_var
961
1015
 
962
1016
  if stmt_level == :stmt_closure
963
- code = "(function() {#{code}; return nil;}).call(this)"
1017
+ code = "(function() {#{code}; return nil;}).call(#{current_self})"
964
1018
  end
965
1019
 
966
1020
  code
@@ -996,23 +1050,25 @@ module Opal
996
1050
  @scope.queue_temp redo_var
997
1051
 
998
1052
  if stmt_level == :stmt_closure
999
- code = "(function() {#{code}; return nil;}).call(this)"
1053
+ code = "(function() {#{code}; return nil;}).call(#{current_self})"
1000
1054
  end
1001
1055
 
1002
1056
  code
1003
1057
  end
1004
1058
 
1005
- ##
1006
1059
  # alias foo bar
1007
1060
  #
1008
1061
  # s(:alias, s(:lit, :foo), s(:lit, :bar))
1009
1062
  def process_alias(exp, level)
1010
- @helpers['alias'] = true
1011
1063
  new = mid_to_jsid exp[0][1].to_s
1012
1064
  old = mid_to_jsid exp[1][1].to_s
1013
- # "__alias(this, #{new.inspect}, #{old.inspect})"
1014
- @scope.methods << new
1015
- "def.#{new} = def.#{old}"
1065
+
1066
+ if [:class, :module].include? @scope.type
1067
+ @scope.methods << new
1068
+ "%s.%s = %s.%s" % [@scope.proto, new, @scope.proto, old]
1069
+ else
1070
+ "def.%s = def.%s" % [new, old]
1071
+ end
1016
1072
  end
1017
1073
 
1018
1074
  def process_masgn(sexp, level)
@@ -1080,7 +1136,7 @@ module Opal
1080
1136
  ivar = exp[0]
1081
1137
  rhs = exp[1]
1082
1138
  ivar = ivar.to_s[1..-1]
1083
- lhs = RESERVED.include?(ivar) ? "this['#{ivar}']" : "this.#{ivar}"
1139
+ lhs = RESERVED.include?(ivar) ? "#{current_self}['#{ivar}']" : "#{current_self}.#{ivar}"
1084
1140
  "#{lhs} = #{process rhs, :expr}"
1085
1141
  end
1086
1142
 
@@ -1089,19 +1145,19 @@ module Opal
1089
1145
  ivar = exp.shift.to_s[1..-1]
1090
1146
  part = RESERVED.include?(ivar) ? "['#{ivar}']" : ".#{ivar}"
1091
1147
  @scope.add_ivar part
1092
- "this#{part}"
1148
+ "#{current_self}#{part}"
1093
1149
  end
1094
1150
 
1095
1151
  # s(:gvar, gvar)
1096
1152
  def process_gvar(sexp, level)
1097
- gvar = sexp.shift.to_s
1153
+ gvar = sexp.shift.to_s[1..-1]
1098
1154
  @helpers['gvars'] = true
1099
1155
  "__gvars[#{gvar.inspect}]"
1100
1156
  end
1101
1157
 
1102
1158
  # s(:gasgn, :gvar, rhs)
1103
1159
  def process_gasgn(sexp, level)
1104
- gvar = sexp[0]
1160
+ gvar = sexp[0].to_s[1..-1]
1105
1161
  rhs = sexp[1]
1106
1162
  @helpers['gvars'] = true
1107
1163
  "__gvars[#{gvar.to_s.inspect}] = #{process rhs, :expr}"
@@ -1109,11 +1165,7 @@ module Opal
1109
1165
 
1110
1166
  # s(:const, :const)
1111
1167
  def process_const(sexp, level)
1112
- if @debug
1113
- "__const_get(__scope, #{sexp.shift.to_s.inspect})"
1114
- else
1115
- "__scope.#{sexp.shift}"
1116
- end
1168
+ "__scope.#{sexp.shift}"
1117
1169
  end
1118
1170
 
1119
1171
  # s(:cdecl, :const, rhs)
@@ -1201,12 +1253,21 @@ module Opal
1201
1253
  falsy = returns(falsy || s(:nil))
1202
1254
  end
1203
1255
 
1204
- code = "if (#{js_truthy test}) {\n"
1256
+ # optimize unless (we don't want else unless we need to)
1257
+ if falsy and !truthy
1258
+ truthy = falsy
1259
+ falsy = nil
1260
+ check = js_falsy test
1261
+ else
1262
+ check = js_truthy test
1263
+ end
1264
+
1265
+ code = "if (#{check}) {\n"
1205
1266
  indent { code += @indent + process(truthy, :stmt) } if truthy
1206
1267
  indent { code += "\n#@indent} else {\n#@indent#{process falsy, :stmt}" } if falsy
1207
1268
  code += "\n#@indent}"
1208
1269
 
1209
- code = "(function() { #{code}; return nil; }).call(this)" if returnable
1270
+ code = "(function() { #{code}; return nil; }).call(#{current_self})" if returnable
1210
1271
 
1211
1272
  code
1212
1273
  end
@@ -1218,7 +1279,12 @@ module Opal
1218
1279
  return process sexp, :expr
1219
1280
  elsif COMPARE.include? mid.to_s
1220
1281
  return process sexp, :expr
1282
+ elsif mid == :"=="
1283
+ return process sexp, :expr
1221
1284
  end
1285
+ elsif [:lvar, :self].include? sexp.first
1286
+ name = process sexp, :expr
1287
+ "#{name} !== false && #{name} !== nil"
1222
1288
  end
1223
1289
  end
1224
1290
 
@@ -1227,10 +1293,22 @@ module Opal
1227
1293
  return optimized
1228
1294
  end
1229
1295
 
1230
- tmp = @scope.new_temp
1231
- @scope.queue_temp tmp
1296
+ with_temp do |tmp|
1297
+ "(%s = %s) !== false && %s !== nil" % [tmp, process(sexp, :expr), tmp]
1298
+ end
1299
+ end
1232
1300
 
1233
- "(%s = %s) !== false && %s !== nil" % [tmp, process(sexp, :expr), tmp]
1301
+ def js_falsy(sexp)
1302
+ if sexp.first == :call
1303
+ mid = sexp[2]
1304
+ if mid == :block_given?
1305
+ return handle_block_given(sexp, true)
1306
+ end
1307
+ end
1308
+
1309
+ with_temp do |tmp|
1310
+ "(%s = %s) === false || %s === nil" % [tmp, process(sexp, :expr), tmp]
1311
+ end
1234
1312
  end
1235
1313
 
1236
1314
  # s(:and, lhs, rhs)
@@ -1272,36 +1350,48 @@ module Opal
1272
1350
  end
1273
1351
 
1274
1352
  # s(:yield, arg1, arg2)
1275
- #
1276
- # FIXME: yield as an expression (when used with js_return) should have the
1277
- # right action. We should then warn when used as an expression in other cases
1278
- # that we would need to use a try/catch/throw block (which is slow and bad
1279
- # mmmkay).
1280
1353
  def process_yield(sexp, level)
1354
+ call = handle_yield_call sexp, level
1355
+
1356
+ if level == :stmt
1357
+ "if (#{call} === __breaker) return __breaker.$v"
1358
+ else
1359
+ call
1360
+ end
1361
+ end
1362
+
1363
+ # Created by `#returns()` for when a yield statement should return
1364
+ # it's value (its last in a block etc).
1365
+ def process_returnable_yield(sexp, level)
1366
+ call = handle_yield_call sexp, level
1367
+
1368
+ with_temp do |tmp|
1369
+ "return %s = #{call}, %s === __breaker ? __breaker.$v : %s" %
1370
+ [tmp, tmp, tmp]
1371
+ end
1372
+ end
1373
+
1374
+ def handle_yield_call(sexp, level)
1281
1375
  @scope.uses_block!
1376
+
1282
1377
  splat = sexp.any? { |s| s.first == :splat }
1283
1378
  sexp.unshift s(:js_tmp, '__context') unless splat
1284
- args = process_arglist(sexp, level)
1379
+ args = process_arglist sexp, level
1285
1380
 
1286
- yielder = @scope.block_name || '__yield'
1381
+ y = @scope.block_name || '__yield'
1287
1382
 
1288
- if splat
1289
- "#{yielder}.apply(__context, #{args})"
1290
- else
1291
- "#{yielder}.call(#{args})"
1292
- end
1383
+ splat ? "#{y}.apply(__context, #{args})" : "#{y}.call(#{args})"
1293
1384
  end
1294
1385
 
1295
1386
  def process_break(exp, level)
1296
1387
  val = exp.empty? ? 'nil' : process(exp.shift, :expr)
1297
1388
  if in_while?
1298
- if @while_loop[:closure]
1299
- "return #{val};"
1300
- else
1301
- "break;"
1302
- end
1389
+ @while_loop[:closure] ? "return #{ val };" : "break;"
1390
+ elsif @scope.iter?
1391
+ error "break must be used as a statement" unless level == :stmt
1392
+ "return (__breaker.$v = #{ val }, __breaker)"
1303
1393
  else
1304
- "return (__breaker.$v = #{val}, __breaker)"
1394
+ error "cannot use break outside of iter/while"
1305
1395
  end
1306
1396
  end
1307
1397
 
@@ -1330,8 +1420,8 @@ module Opal
1330
1420
 
1331
1421
  code << "else {return nil}" if returnable and !done_else
1332
1422
 
1333
- code = "$case = #{expr};#{code.join "\n"}"
1334
- code = "(function() { #{code} }).call(this)" if returnable
1423
+ code = "$case = #{expr};#{code.join @space}"
1424
+ code = "(function() { #{code} }).call(#{current_self})" if returnable
1335
1425
  code
1336
1426
  end
1337
1427
 
@@ -1352,7 +1442,7 @@ module Opal
1352
1442
  call = s(:call, s(:js_tmp, "$splt[i]"), :===, s(:arglist, s(:js_tmp, "$case")))
1353
1443
  splt = "(function($splt) {for(var i = 0; i < $splt.length; i++) {"
1354
1444
  splt += "if (#{process call, :expr}) { return true; }"
1355
- splt += "} return false; }).call(this, #{process a[1], :expr})"
1445
+ splt += "} return false; }).call(#{current_self}, #{process a[1], :expr})"
1356
1446
 
1357
1447
  test << splt
1358
1448
  else
@@ -1364,7 +1454,7 @@ module Opal
1364
1454
  end
1365
1455
  end
1366
1456
 
1367
- "if (%s) {\n%s\n}" % [test.join(' || '), body]
1457
+ "if (%s) {%s%s%s}" % [test.join(' || '), @space, body, @space]
1368
1458
  end
1369
1459
 
1370
1460
  # lhs =~ rhs
@@ -1381,9 +1471,10 @@ module Opal
1381
1471
  #
1382
1472
  # s(:cvar, name)
1383
1473
  def process_cvar(exp, level)
1384
- tmp = @scope.new_temp
1385
- @scope.queue_temp tmp
1386
- "((%s = Opal.cvars[%s]) == null ? nil : %s)" % [tmp, exp.shift.to_s.inspect, tmp]
1474
+ with_temp do |tmp|
1475
+ "((%s = Opal.cvars[%s]) == null ? nil : %s)" %
1476
+ [tmp, exp.shift.to_s.inspect, tmp]
1477
+ end
1387
1478
  end
1388
1479
 
1389
1480
  # @@name = rhs
@@ -1432,15 +1523,16 @@ module Opal
1432
1523
  def js_super args
1433
1524
  if @scope.type == :def
1434
1525
  identity = @scope.identify!
1526
+ cls_name = @scope.parent.name
1527
+ jsid = mid_to_jsid @scope.mid.to_s
1528
+ base = @scope.defs ? '' : ".prototype"
1435
1529
 
1436
- # FIXME: only use `._proto` when inside normal def. remove it
1437
- # for `def self.foo`.
1438
- "__class._super._proto.#{@scope.mid}.apply(this, #{args})"
1530
+ "%s._super%s.%s.apply(this, %s)" % [cls_name, base, jsid, args]
1439
1531
 
1440
1532
  elsif @scope.type == :iter
1441
1533
  chain, defn, mid = @scope.get_super_chain
1442
- trys = chain.map { |c| "#{c}._jsid" }.join ' || '
1443
- "this._klass._super._proto[#{trys} || #{mid}].apply(this, #{args})"
1534
+ trys = chain.map { |c| "#{c}._sup" }.join ' || '
1535
+ "(#{trys} || this._klass._super._proto[#{mid}]).apply(this, #{args})"
1444
1536
 
1445
1537
  else
1446
1538
  raise "Cannot call super() from outside a method block"
@@ -1470,14 +1562,13 @@ module Opal
1470
1562
  if op.to_s == "||"
1471
1563
  raise "op_asgn2 for ||"
1472
1564
  else
1473
- temp = @scope.new_temp
1474
- getr = s(:call, s(:js_tmp, temp), mid, s(:arglist))
1475
- oper = s(:call, getr, op, s(:arglist, rhs))
1476
- asgn = s(:call, s(:js_tmp, temp), "#{mid}=", s(:arglist, oper))
1565
+ with_temp do |temp|
1566
+ getr = s(:call, s(:js_tmp, temp), mid, s(:arglist))
1567
+ oper = s(:call, getr, op, s(:arglist, rhs))
1568
+ asgn = s(:call, s(:js_tmp, temp), "#{mid}=", s(:arglist, oper))
1477
1569
 
1478
- "(#{temp} = #{lhs}, #{process asgn, :expr})".tap {
1479
- @scope.queue_temp temp
1480
- }
1570
+ "(#{temp} = #{lhs}, #{process asgn, :expr})"
1571
+ end
1481
1572
  end
1482
1573
  end
1483
1574
 
@@ -1494,8 +1585,8 @@ module Opal
1494
1585
  ensr = process ensr, level
1495
1586
  body = "try {\n#{body}}" unless body =~ /^try \{/
1496
1587
 
1497
- res = "#{body}\n finally {\n#{ensr}}"
1498
- res = "(function() { #{res}; }).call(this)" if retn
1588
+ res = "#{body}#{@space}finally {#{@space}#{ensr}}"
1589
+ res = "(function() { #{res}; }).call(#{current_self})" if retn
1499
1590
  res
1500
1591
  end
1501
1592
 
@@ -1512,8 +1603,8 @@ module Opal
1512
1603
  # if no rescue statement captures our error, we should rethrow
1513
1604
  parts << "else { throw $err; }"
1514
1605
 
1515
- code = "try {\n#@indent#{body}\n#@indent} catch ($err) {\n#@indent#{parts.join "\n"}\n}"
1516
- code = "(function() { #{code} }).call(this)" if level == :expr
1606
+ code = "try {#@space#{body}#@space} catch ($err) {#@space#{parts.join @space}#{@space}}"
1607
+ code = "(function() { #{code} }).call(#{current_self})" if level == :expr
1517
1608
 
1518
1609
  code
1519
1610
  end
@@ -1538,7 +1629,7 @@ module Opal
1538
1629
  val = process(val, :expr) + ";"
1539
1630
  end
1540
1631
 
1541
- "if (#{err}) {\n#{val}#{body}}"
1632
+ "if (#{err}) {#{@space}#{val}#{body}}"
1542
1633
  # raise exp.inspect
1543
1634
  end
1544
1635