opal 0.3.19 → 0.3.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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