opal 1.3.1 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -0
  3. data/.github/workflows/build.yml +20 -21
  4. data/.rubocop.yml +5 -1
  5. data/CHANGELOG.md +119 -11
  6. data/UNRELEASED.md +4 -9
  7. data/benchmark-ips/bm_truthy.rb +30 -0
  8. data/bin/opal-mspec +1 -3
  9. data/bin/opal-repl +1 -2
  10. data/bin/remove-filters +1 -4
  11. data/docs/compiled_ruby.md +37 -22
  12. data/docs/faq.md +1 -1
  13. data/docs/headless_chrome.md +11 -21
  14. data/docs/jquery.md +1 -6
  15. data/docs/opal_parser.md +3 -1
  16. data/docs/promises.md +2 -0
  17. data/docs/releasing.md +3 -0
  18. data/docs/roda-sprockets.md +0 -1
  19. data/docs/source_maps.md +10 -11
  20. data/docs/static_applications.md +2 -2
  21. data/docs/unsupported_features.md +4 -0
  22. data/exe/opal-repl +1 -3
  23. data/lib/opal/ast/builder.rb +1 -1
  24. data/lib/opal/cli.rb +2 -2
  25. data/lib/opal/cli_runners/nodejs.rb +9 -2
  26. data/lib/opal/cli_runners/source-map-support-browser.js +80 -216
  27. data/lib/opal/cli_runners/source-map-support-node.js +80 -216
  28. data/lib/opal/cli_runners/source-map-support.js +5 -1
  29. data/lib/opal/cli_runners/system_runner.rb +10 -4
  30. data/lib/opal/compiler.rb +3 -5
  31. data/lib/opal/fragment.rb +5 -1
  32. data/lib/opal/nodes/args/extract_block_arg.rb +1 -8
  33. data/lib/opal/nodes/args/extract_kwoptarg.rb +1 -3
  34. data/lib/opal/nodes/args/extract_optarg.rb +1 -3
  35. data/lib/opal/nodes/args/extract_post_arg.rb +2 -5
  36. data/lib/opal/nodes/args/extract_post_optarg.rb +2 -7
  37. data/lib/opal/nodes/args/initialize_iterarg.rb +1 -3
  38. data/lib/opal/nodes/args/prepare_post_args.rb +5 -1
  39. data/lib/opal/nodes/base.rb +3 -2
  40. data/lib/opal/nodes/call.rb +20 -9
  41. data/lib/opal/nodes/call_special.rb +50 -0
  42. data/lib/opal/nodes/class.rb +24 -15
  43. data/lib/opal/nodes/constants.rb +23 -5
  44. data/lib/opal/nodes/def.rb +20 -23
  45. data/lib/opal/nodes/defined.rb +5 -5
  46. data/lib/opal/nodes/definitions.rb +2 -2
  47. data/lib/opal/nodes/defs.rb +2 -5
  48. data/lib/opal/nodes/helpers.rb +48 -18
  49. data/lib/opal/nodes/if.rb +113 -8
  50. data/lib/opal/nodes/iter.rb +23 -16
  51. data/lib/opal/nodes/literal.rb +18 -4
  52. data/lib/opal/nodes/logic.rb +2 -1
  53. data/lib/opal/nodes/masgn.rb +4 -9
  54. data/lib/opal/nodes/module.rb +29 -19
  55. data/lib/opal/nodes/node_with_args.rb +1 -7
  56. data/lib/opal/nodes/scope.rb +54 -15
  57. data/lib/opal/nodes/singleton_class.rb +5 -3
  58. data/lib/opal/nodes/super.rb +12 -12
  59. data/lib/opal/nodes/top.rb +34 -31
  60. data/lib/opal/nodes/variables.rb +2 -2
  61. data/lib/opal/nodes/x_string.rb +30 -28
  62. data/lib/opal/nodes.rb +0 -1
  63. data/lib/opal/parser/patch.rb +75 -0
  64. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  65. data/lib/opal/regexp_anchors.rb +7 -7
  66. data/lib/opal/requires.rb +19 -0
  67. data/lib/opal/rewriters/pattern_matching.rb +1 -1
  68. data/lib/opal/rewriters/returnable_logic.rb +102 -4
  69. data/lib/opal/util.rb +2 -2
  70. data/lib/opal/version.rb +1 -1
  71. data/lib/opal.rb +1 -17
  72. data/opal/corelib/array/pack.rb +11 -11
  73. data/opal/corelib/array.rb +193 -152
  74. data/opal/corelib/basic_object.rb +19 -15
  75. data/opal/corelib/binding.rb +7 -7
  76. data/opal/corelib/boolean.rb +12 -15
  77. data/opal/corelib/class.rb +23 -1
  78. data/opal/corelib/comparable.rb +8 -8
  79. data/opal/corelib/complex/base.rb +2 -2
  80. data/opal/corelib/complex.rb +79 -88
  81. data/opal/corelib/constants.rb +9 -9
  82. data/opal/corelib/dir.rb +4 -3
  83. data/opal/corelib/enumerable.rb +140 -127
  84. data/opal/corelib/enumerator/arithmetic_sequence.rb +177 -0
  85. data/opal/corelib/enumerator/chain.rb +42 -0
  86. data/opal/corelib/enumerator/generator.rb +35 -0
  87. data/opal/corelib/enumerator/lazy.rb +243 -0
  88. data/opal/corelib/enumerator/yielder.rb +36 -0
  89. data/opal/corelib/enumerator.rb +45 -300
  90. data/opal/corelib/error/errno.rb +47 -0
  91. data/opal/corelib/error.rb +62 -60
  92. data/opal/corelib/file.rb +26 -12
  93. data/opal/corelib/hash.rb +98 -107
  94. data/opal/corelib/helpers.rb +62 -13
  95. data/opal/corelib/io.rb +48 -35
  96. data/opal/corelib/kernel/format.rb +29 -29
  97. data/opal/corelib/kernel.rb +86 -83
  98. data/opal/corelib/main.rb +14 -12
  99. data/opal/corelib/marshal/read_buffer.rb +15 -15
  100. data/opal/corelib/marshal/write_buffer.rb +45 -44
  101. data/opal/corelib/marshal.rb +3 -3
  102. data/opal/corelib/math.rb +50 -50
  103. data/opal/corelib/method.rb +12 -8
  104. data/opal/corelib/module.rb +79 -75
  105. data/opal/corelib/nil.rb +9 -11
  106. data/opal/corelib/number.rb +113 -118
  107. data/opal/corelib/numeric.rb +37 -33
  108. data/opal/corelib/object_space.rb +11 -10
  109. data/opal/corelib/pack_unpack/format_string_parser.rb +3 -3
  110. data/opal/corelib/pattern_matching/base.rb +7 -7
  111. data/opal/corelib/pattern_matching.rb +1 -1
  112. data/opal/corelib/proc.rb +15 -16
  113. data/opal/corelib/process/base.rb +2 -2
  114. data/opal/corelib/process/status.rb +21 -0
  115. data/opal/corelib/process.rb +5 -5
  116. data/opal/corelib/random/formatter.rb +11 -11
  117. data/opal/corelib/random/math_random.js.rb +1 -1
  118. data/opal/corelib/random/mersenne_twister.rb +3 -3
  119. data/opal/corelib/random/seedrandom.js.rb +3 -3
  120. data/opal/corelib/random.rb +17 -17
  121. data/opal/corelib/range.rb +51 -35
  122. data/opal/corelib/rational/base.rb +4 -4
  123. data/opal/corelib/rational.rb +61 -62
  124. data/opal/corelib/regexp.rb +54 -45
  125. data/opal/corelib/runtime.js +247 -141
  126. data/opal/corelib/string/encoding.rb +21 -21
  127. data/opal/corelib/string/unpack.rb +19 -14
  128. data/opal/corelib/string.rb +137 -130
  129. data/opal/corelib/struct.rb +59 -46
  130. data/opal/corelib/time.rb +47 -57
  131. data/opal/corelib/trace_point.rb +2 -2
  132. data/opal/corelib/unsupported.rb +31 -120
  133. data/opal/corelib/variables.rb +3 -3
  134. data/opal/opal/base.rb +9 -8
  135. data/opal/opal/full.rb +8 -8
  136. data/opal/opal/mini.rb +17 -17
  137. data/opal/opal.rb +17 -18
  138. data/opal.gemspec +1 -1
  139. data/spec/filters/bugs/array.rb +4 -24
  140. data/spec/filters/bugs/basicobject.rb +0 -1
  141. data/spec/filters/bugs/bigdecimal.rb +0 -23
  142. data/spec/filters/bugs/binding.rb +0 -1
  143. data/spec/filters/bugs/boolean.rb +3 -0
  144. data/spec/filters/bugs/class.rb +2 -0
  145. data/spec/filters/bugs/date.rb +0 -5
  146. data/spec/filters/bugs/encoding.rb +8 -50
  147. data/spec/filters/bugs/enumerable.rb +4 -1
  148. data/spec/filters/bugs/enumerator.rb +3 -36
  149. data/spec/filters/bugs/exception.rb +0 -2
  150. data/spec/filters/bugs/file.rb +0 -2
  151. data/spec/filters/bugs/float.rb +0 -3
  152. data/spec/filters/bugs/hash.rb +5 -3
  153. data/spec/filters/bugs/integer.rb +2 -3
  154. data/spec/filters/bugs/kernel.rb +2 -31
  155. data/spec/filters/bugs/language.rb +29 -49
  156. data/spec/filters/bugs/main.rb +0 -2
  157. data/spec/filters/bugs/marshal.rb +2 -3
  158. data/spec/filters/bugs/matrix.rb +0 -36
  159. data/spec/filters/bugs/module.rb +7 -61
  160. data/spec/filters/bugs/numeric.rb +0 -7
  161. data/spec/filters/bugs/objectspace.rb +1 -1
  162. data/spec/filters/bugs/pack_unpack.rb +0 -4
  163. data/spec/filters/bugs/proc.rb +0 -9
  164. data/spec/filters/bugs/random.rb +0 -5
  165. data/spec/filters/bugs/range.rb +1 -6
  166. data/spec/filters/bugs/regexp.rb +0 -3
  167. data/spec/filters/bugs/set.rb +8 -1
  168. data/spec/filters/bugs/string.rb +9 -34
  169. data/spec/filters/bugs/stringscanner.rb +8 -7
  170. data/spec/filters/bugs/struct.rb +2 -3
  171. data/spec/filters/bugs/symbol.rb +0 -1
  172. data/spec/filters/bugs/time.rb +0 -8
  173. data/spec/filters/bugs/unboundmethod.rb +0 -8
  174. data/spec/filters/bugs/warnings.rb +1 -7
  175. data/spec/filters/unsupported/freeze.rb +24 -0
  176. data/spec/filters/unsupported/integer.rb +1 -0
  177. data/spec/filters/unsupported/kernel.rb +12 -0
  178. data/spec/filters/unsupported/privacy.rb +3 -0
  179. data/spec/filters/unsupported/string.rb +2 -0
  180. data/spec/lib/builder_spec.rb +2 -2
  181. data/spec/lib/cli_spec.rb +1 -1
  182. data/spec/lib/compiler_spec.rb +37 -37
  183. data/spec/lib/simple_server_spec.rb +2 -2
  184. data/spec/lib/source_map/file_spec.rb +1 -1
  185. data/spec/opal/compiler/irb_spec.rb +2 -2
  186. data/spec/opal/core/io/read_spec.rb +69 -0
  187. data/spec/opal/core/kernel/puts_spec.rb +90 -0
  188. data/spec/opal/core/language/super_spec.rb +21 -0
  189. data/spec/opal/core/language/xstring_spec.rb +13 -0
  190. data/spec/opal/core/language_spec.rb +14 -0
  191. data/spec/opal/core/string/gsub_spec.rb +8 -0
  192. data/spec/ruby_specs +4 -2
  193. data/spec/support/rewriters_helper.rb +1 -1
  194. data/stdlib/bigdecimal.rb +7 -11
  195. data/stdlib/buffer/view.rb +2 -2
  196. data/stdlib/buffer.rb +2 -2
  197. data/stdlib/date.rb +5 -6
  198. data/stdlib/erb.rb +1 -0
  199. data/stdlib/js.rb +2 -1
  200. data/stdlib/native.rb +7 -8
  201. data/stdlib/nodejs/argf.rb +4 -4
  202. data/stdlib/nodejs/base.rb +29 -0
  203. data/stdlib/nodejs/dir.rb +1 -1
  204. data/stdlib/nodejs/env.rb +6 -9
  205. data/stdlib/nodejs/file.rb +23 -17
  206. data/stdlib/nodejs/fileutils.rb +3 -3
  207. data/stdlib/nodejs/io.rb +2 -20
  208. data/stdlib/nodejs/irb.rb +0 -0
  209. data/stdlib/nodejs/kernel.rb +2 -37
  210. data/stdlib/nodejs.rb +1 -3
  211. data/stdlib/opal/miniracer.rb +2 -0
  212. data/stdlib/opal/platform.rb +6 -13
  213. data/stdlib/opal/replutils.rb +16 -5
  214. data/stdlib/opal-parser.rb +2 -2
  215. data/stdlib/optparse/ac.rb +54 -0
  216. data/stdlib/optparse/date.rb +14 -0
  217. data/stdlib/optparse/kwargs.rb +22 -0
  218. data/stdlib/optparse/shellwords.rb +7 -0
  219. data/stdlib/optparse/time.rb +15 -0
  220. data/stdlib/optparse/uri.rb +7 -0
  221. data/stdlib/optparse/version.rb +69 -0
  222. data/stdlib/optparse.rb +2279 -0
  223. data/stdlib/pathname.rb +5 -6
  224. data/stdlib/pp.rb +18 -2
  225. data/stdlib/promise/v2.rb +18 -29
  226. data/stdlib/promise.rb +15 -21
  227. data/stdlib/quickjs/io.rb +0 -2
  228. data/stdlib/quickjs/kernel.rb +0 -2
  229. data/stdlib/quickjs.rb +2 -0
  230. data/stdlib/set.rb +32 -32
  231. data/stdlib/shellwords.rb +240 -0
  232. data/stdlib/stringio.rb +3 -6
  233. data/stdlib/strscan.rb +5 -8
  234. data/stdlib/template.rb +2 -2
  235. data/stdlib/thread.rb +7 -9
  236. data/tasks/linting-parse-eslint-results.js +1 -0
  237. data/tasks/linting.rake +0 -10
  238. data/tasks/performance.rake +5 -2
  239. data/tasks/testing/mspec_special_calls.rb +0 -12
  240. data/tasks/testing.rake +55 -37
  241. data/test/nodejs/test_file.rb +11 -0
  242. metadata +55 -8
  243. data/lib/opal/nodes/case.rb +0 -114
data/lib/opal/nodes/if.rb CHANGED
@@ -10,10 +10,30 @@ module Opal
10
10
  children :test, :true_body, :false_body
11
11
 
12
12
  def compile
13
+ if should_compile_as_simple_expression?
14
+ if true_body == s(:true)
15
+ compile_with_binary_or
16
+ elsif false_body == s(:false)
17
+ compile_with_binary_and
18
+ else
19
+ compile_with_ternary
20
+ end
21
+ else
22
+ compile_with_if
23
+ end
24
+ end
25
+
26
+ def compile_with_if
13
27
  truthy = self.truthy
14
28
  falsy = self.falsy
15
29
 
16
- push 'if (', js_truthy(test), ') {'
30
+ if falsy && !truthy
31
+ # Let's optimize a little bit `unless` calls.
32
+ push 'if (!', js_truthy(test), ') {'
33
+ falsy, truthy = truthy, falsy
34
+ else
35
+ push 'if (', js_truthy(test), ') {'
36
+ end
17
37
 
18
38
  # skip if-body if no truthy sexp
19
39
  indent { line stmt(truthy) } if truthy
@@ -30,29 +50,114 @@ module Opal
30
50
  line '}'
31
51
  end
32
52
  else
33
- push '}'
53
+ line '}'
54
+
55
+ # This resolution isn't finite. Let's ensure this block
56
+ # always return something if we expect a return
57
+ line 'return nil;' if expects_expression?
34
58
  end
35
59
 
36
- if needs_wrapper?
60
+ if expects_expression?
37
61
  if scope.await_encountered
38
- wrap '(await (async function() {', '; return nil; })())'
62
+ wrap '(await (async function() {', '})())'
39
63
  else
40
- wrap '(function() {', '; return nil; })()'
64
+ wrap '(function() {', '})()'
41
65
  end
42
66
  end
43
67
  end
44
68
 
45
69
  def truthy
46
- needs_wrapper? ? compiler.returns(true_body || s(:nil)) : true_body
70
+ returnify(true_body)
47
71
  end
48
72
 
49
73
  def falsy
50
- needs_wrapper? ? compiler.returns(false_body || s(:nil)) : false_body
74
+ returnify(false_body)
75
+ end
76
+
77
+ def returnify(body)
78
+ if expects_expression? && body
79
+ compiler.returns(body)
80
+ else
81
+ body
82
+ end
51
83
  end
52
84
 
53
- def needs_wrapper?
85
+ def expects_expression?
54
86
  expr? || recv?
55
87
  end
88
+
89
+ # There was a particular case in the past, that when we
90
+ # expected an expression from if, we always had to closure
91
+ # it. This produced an ugly code that was hard to minify.
92
+ # This addition tries to make a few cases compiled with
93
+ # a ternary operator instead and possibly a binary operator
94
+ # even?
95
+ def should_compile_as_simple_expression?
96
+ expects_expression? && simple?(true_body) && simple?(false_body)
97
+ end
98
+
99
+ def compile_with_ternary
100
+ truthy = true_body
101
+ falsy = false_body
102
+
103
+ push '('
104
+
105
+ push js_truthy(test), ' ? '
106
+
107
+ push '(', expr(truthy || s(:nil)), ') : '
108
+ if !falsy || falsy.type == :if
109
+ push expr(falsy || s(:nil))
110
+ else
111
+ push '(', expr(falsy || s(:nil)), ')'
112
+ end
113
+
114
+ push ')'
115
+ end
116
+
117
+ def compile_with_binary_and
118
+ if sexp.meta[:do_js_truthy_on_true_body]
119
+ truthy = js_truthy(true_body || s(:nil))
120
+ else
121
+ truthy = expr(true_body || s(:nil))
122
+ end
123
+
124
+ push '('
125
+ push js_truthy(test), ' && '
126
+ push '(', truthy, ')'
127
+ push ')'
128
+ end
129
+
130
+ def compile_with_binary_or
131
+ if sexp.meta[:do_js_truthy_on_false_body]
132
+ falsy = js_truthy(false_body || s(:nil))
133
+ else
134
+ falsy = expr(false_body || s(:nil))
135
+ end
136
+
137
+ push '('
138
+ push js_truthy(test), ' || '
139
+ push '(', falsy, ')'
140
+ push ')'
141
+ end
142
+
143
+ # Let's ensure there are no control flow statements inside.
144
+ def simple?(body)
145
+ case body
146
+ when AST::Node
147
+ case body.type
148
+ when :return, :js_return, :break, :next, :redo, :retry
149
+ false
150
+ when :xstr
151
+ XStringNode.single_line?(
152
+ XStringNode.strip_empty_children(body.children)
153
+ )
154
+ else
155
+ body.children.all? { |i| simple?(i) }
156
+ end
157
+ else
158
+ true
159
+ end
160
+ end
56
161
  end
57
162
 
58
163
  class IFlipFlop < Base
@@ -19,13 +19,15 @@ module Opal
19
19
 
20
20
  in_scope do
21
21
  identity = scope.identify!
22
- add_temp "self = #{identity}.$$s == null ? this : #{identity}.$$s"
23
22
 
24
23
  inline_params = process(inline_args)
25
24
 
26
25
  compile_arity_check
27
26
 
28
27
  body_code = stmt(returned_body)
28
+
29
+ add_temp "self = #{identity}.$$s == null ? this : #{identity}.$$s" if @define_self
30
+
29
31
  to_vars = scope.to_vars
30
32
 
31
33
  line body_code
@@ -40,16 +42,19 @@ module Opal
40
42
  unshift to_vars
41
43
 
42
44
  if await_encountered
43
- unshift "(#{identity} = async function(", inline_params, '){'
45
+ unshift "async function #{identity}(", inline_params, '){'
44
46
  else
45
- unshift "(#{identity} = function(", inline_params, '){'
47
+ unshift "function #{identity}(", inline_params, '){'
46
48
  end
47
- push "}, #{identity}.$$s = self,"
48
- push " #{identity}.$$brk = $brk," if contains_break?
49
- push " #{identity}.$$arity = #{arity},"
49
+ push '}'
50
+
51
+ blockopts = []
52
+ blockopts << "$$arity: #{arity}"
53
+ blockopts << "$$s: #{scope.self}" if @define_self
54
+ blockopts << "$$brk: $brk" if contains_break?
50
55
 
51
56
  if compiler.arity_check?
52
- push " #{identity}.$$parameters = #{parameters_code},"
57
+ blockopts << "$$parameters: #{parameters_code}"
53
58
  end
54
59
 
55
60
  # MRI expands a passed argument if the block:
@@ -61,24 +66,26 @@ module Opal
61
66
  # This flag on the method indicates that a block has a top level mlhs argument
62
67
  # which means that we have to expand passed array explicitly in runtime.
63
68
  if has_top_level_mlhs_arg?
64
- push " #{identity}.$$has_top_level_mlhs_arg = true,"
69
+ blockopts << "$$has_top_level_mlhs_arg: true"
65
70
  end
66
71
 
67
72
  if has_trailing_comma_in_args?
68
- push " #{identity}.$$has_trailing_comma_in_args = true,"
73
+ blockopts << "$$has_trailing_comma_in_args: true"
69
74
  end
70
75
 
71
- push " #{identity})"
76
+ if blockopts.length == 1
77
+ push ", #{arity}"
78
+ elsif blockopts.length > 1
79
+ push ', {', blockopts.join(', '), '}'
80
+ end
81
+
82
+ scope.nesting if @define_nesting
83
+ scope.relative_access if @define_relative_access
72
84
  end
73
85
 
74
86
  def compile_block_arg
75
87
  if block_arg
76
- scope.block_name = block_arg
77
- scope.add_temp block_arg
78
- scope_name = scope.identify!
79
-
80
- line "#{block_arg} = #{scope_name}.$$p || nil;"
81
- line "if (#{block_arg}) #{scope_name}.$$p = null;"
88
+ scope.prepare_block
82
89
  end
83
90
  end
84
91
 
@@ -8,7 +8,11 @@ module Opal
8
8
  handle :true, :false, :self, :nil
9
9
 
10
10
  def compile
11
- push type.to_s
11
+ if type == :self
12
+ push scope.self
13
+ else
14
+ push type.to_s
15
+ end
12
16
  end
13
17
 
14
18
  def self.truthy_optimize?
@@ -137,7 +141,9 @@ module Opal
137
141
  end
138
142
 
139
143
  def compile_dynamic_regexp
140
- push 'Opal.regexp(['
144
+ helper :regexp
145
+
146
+ push '$regexp(['
141
147
  value.children.each_with_index do |v, index|
142
148
  push ', ' unless index.zero?
143
149
  push expr(v)
@@ -229,10 +235,18 @@ module Opal
229
235
  handle :dstr
230
236
 
231
237
  def compile
232
- push '""'
238
+ if children.length > 1 && children.first.type == :str
239
+ skip_empty = true
240
+ else
241
+ push '""'
242
+ end
233
243
 
234
244
  children.each do |part|
235
- push ' + '
245
+ if skip_empty
246
+ skip_empty = false
247
+ else
248
+ push ' + '
249
+ end
236
250
 
237
251
  if part.type == :str
238
252
  push expr(part)
@@ -104,7 +104,8 @@ module Opal
104
104
  if empty_splat?
105
105
  push '[]'
106
106
  else
107
- push 'Opal.to_a(', recv(value), ')'
107
+ helper :to_a
108
+ push '$to_a(', recv(value), ')'
108
109
  end
109
110
  end
110
111
  end
@@ -18,17 +18,11 @@ module Opal
18
18
  rhs_len = rhs.children.any? { |c| c.type == :splat } ? nil : rhs.children.size
19
19
  compile_masgn(lhs.children, array, rhs_len)
20
20
  push ", #{array}" # a mass assignment evaluates to the RHS
21
- elsif rhs.type == :begin
22
- retval = scope.new_temp
23
- push "#{retval} = ", expr(rhs)
24
- push ", #{array} = Opal.to_ary(#{retval})"
25
- compile_masgn(lhs.children, array)
26
- push ", #{retval}"
27
- scope.queue_temp(retval)
28
21
  else
22
+ helper :to_ary
29
23
  retval = scope.new_temp
30
24
  push "#{retval} = ", expr(rhs)
31
- push ", #{array} = Opal.to_ary(#{retval})"
25
+ push ", #{array} = $to_ary(#{retval})"
32
26
  compile_masgn(lhs.children, array)
33
27
  push ", #{retval}"
34
28
  scope.queue_temp(retval)
@@ -97,9 +91,10 @@ module Opal
97
91
  elsif child.type == :attrasgn
98
92
  part.last << assign
99
93
  elsif child.type == :mlhs
94
+ helper :to_ary
100
95
  # nested destructuring
101
96
  tmp = scope.new_temp
102
- push ", (#{tmp} = Opal.to_ary(#{assign.children[0]})"
97
+ push ", (#{tmp} = $to_ary(#{assign.children[0]})"
103
98
  compile_masgn(child.children, tmp)
104
99
  push ')'
105
100
  scope.queue_temp(tmp)
@@ -13,23 +13,32 @@ module Opal
13
13
  name, base = name_and_base
14
14
  helper :module
15
15
 
16
- line " var self = $module($base, '#{name}');"
17
- in_scope do
18
- scope.name = name
19
- compile_body
20
- end
21
-
22
- if await_encountered
23
- await_begin = '(await '
24
- await_end = ')'
25
- async = 'async '
26
- parent.await_encountered = true
16
+ if body.nil?
17
+ # Simplified compile for empty body
18
+ if stmt?
19
+ unshift '$module(', base, ", '#{name}')"
20
+ else
21
+ unshift '($module(', base, ", '#{name}'), nil)"
22
+ end
27
23
  else
28
- await_begin, await_end, async = '', '', ''
29
- end
24
+ line " var self = $module($base, '#{name}');"
25
+ in_scope do
26
+ scope.name = name
27
+ compile_body
28
+ end
29
+
30
+ if await_encountered
31
+ await_begin = '(await '
32
+ await_end = ')'
33
+ async = 'async '
34
+ parent.await_encountered = true
35
+ else
36
+ await_begin, await_end, async = '', '', ''
37
+ end
30
38
 
31
- unshift "#{await_begin}(#{async}function($base, $parent_nesting) {"
32
- line '})(', base, ", $nesting)#{await_end}"
39
+ unshift "#{await_begin}(#{async}function($base#{', $parent_nesting' if @define_nesting}) {"
40
+ line '})(', base, "#{', ' + scope.nesting if @define_nesting})#{await_end}"
41
+ end
33
42
  end
34
43
 
35
44
  private
@@ -39,18 +48,19 @@ module Opal
39
48
  base, name = cid.children
40
49
 
41
50
  if base.nil?
42
- [name, '$nesting[0]']
51
+ [name, "#{scope.nesting}[0]"]
43
52
  else
44
53
  [name, expr(base)]
45
54
  end
46
55
  end
47
56
 
48
57
  def compile_body
49
- add_temp '$nesting = [self].concat($parent_nesting)'
50
-
51
- body_code = stmt(compiler.returns(body || s(:nil)))
58
+ body_code = stmt(compiler.returns(body))
52
59
  empty_line
53
60
 
61
+ add_temp "$nesting = [self].concat($parent_nesting)" if @define_nesting
62
+ add_temp '$$ = Opal.$r($nesting)' if @define_relative_access
63
+
54
64
  line scope.to_vars
55
65
  line body_code
56
66
  end
@@ -29,13 +29,7 @@ module Opal
29
29
 
30
30
  def compile_block_arg
31
31
  if scope.uses_block?
32
- scope_name = scope.identity
33
- yielder = scope.block_name || '$yield'
34
-
35
- add_temp "$iter = #{scope_name}.$$p"
36
- add_temp "#{yielder} = $iter || nil"
37
-
38
- line "if ($iter) #{scope_name}.$$p = null;"
32
+ scope.prepare_block
39
33
  end
40
34
  end
41
35
 
@@ -88,11 +88,6 @@ module Opal
88
88
  @type == :top
89
89
  end
90
90
 
91
- # Traverses to the top scope.
92
- def top_scope
93
- top? ? self : parent.top_scope
94
- end
95
-
96
91
  # True if a block/iter scope
97
92
  def iter?
98
93
  @type == :iter
@@ -141,19 +136,21 @@ module Opal
141
136
  "if ($gvars#{gvar} == null) $gvars#{gvar} = nil;\n"
142
137
  end
143
138
 
139
+ if class? && !@proto_ivars.empty?
140
+ vars << '$proto = self.$$prototype'
141
+ end
142
+
144
143
  indent = @compiler.parser_indent
145
144
  str = vars.empty? ? '' : "var #{vars.join ', '};\n"
146
145
  str += "#{indent}#{iv.join indent}" unless ivars.empty?
147
146
  str += "#{indent}#{gv.join indent}" unless gvars.empty?
148
147
 
149
148
  if class? && !@proto_ivars.empty?
150
- pvars = @proto_ivars.map { |i| "self.$$prototype#{i}" }.join(' = ')
151
- result = "#{str}\n#{indent}#{pvars} = nil;"
152
- else
153
- result = str
149
+ pvars = @proto_ivars.map { |i| "$proto#{i}" }.join(' = ')
150
+ str = "#{str}\n#{indent}#{pvars} = nil;"
154
151
  end
155
152
 
156
- fragment(result)
153
+ fragment(str)
157
154
  end
158
155
 
159
156
  def add_scope_ivar(ivar)
@@ -190,7 +187,8 @@ module Opal
190
187
  end
191
188
 
192
189
  def scope_locals
193
- @locals | @args | (@parent && @type == :iter ? @parent.scope_locals : [])
190
+ locals = @locals | @args | (@parent && @type == :iter ? @parent.scope_locals : [])
191
+ locals.reject { |i| i.to_s.start_with?('$') }
194
192
  end
195
193
 
196
194
  def add_scope_temp(tmp)
@@ -251,10 +249,21 @@ module Opal
251
249
  def identify!(name = nil)
252
250
  return @identity if @identity
253
251
 
254
- # Parent scope is the defining module/class
255
- name ||= [(parent && (parent.name || parent.scope_name)), mid].compact.join('_')
256
- @identity = @compiler.unique_temp(name)
257
- @parent.add_scope_temp @identity if @parent
252
+ if valid_name? mid
253
+ # There are some special utf8 chars that can be used as valid JS
254
+ # identifiers, some examples:
255
+ #
256
+ # utf8_pond = 'ⵌ'
257
+ # utf8_question = 'ʔ̣'
258
+ # utf8_exclamation 'ǃ'
259
+ #
260
+ # For now we're just using $$, to maintain compatibility with older IEs.
261
+ @identity = "$$#{mid}"
262
+ else
263
+ # Parent scope is the defining module/class
264
+ name ||= [(parent && (parent.name || parent.scope_name)), mid].compact.join('_')
265
+ @identity = @compiler.unique_temp(name)
266
+ end
258
267
 
259
268
  @identity
260
269
  end
@@ -371,6 +380,36 @@ module Opal
371
380
  [prev, curr]
372
381
  end
373
382
 
383
+ # Returns 'self', but also ensures that the self variable is set
384
+ def self
385
+ @define_self = true
386
+ 'self'
387
+ end
388
+
389
+ # Returns '$nesting', but also ensures we compile the nesting chain
390
+ def nesting
391
+ @define_nesting = true
392
+ '$nesting'
393
+ end
394
+
395
+ # Returns '$$', but also ensures we compile it
396
+ def relative_access
397
+ @define_relative_access = @define_nesting = true
398
+ '$$'
399
+ end
400
+
401
+ def prepare_block(block_name = nil)
402
+ scope_name = scope.identity
403
+ self.block_name = block_name if block_name
404
+
405
+ add_temp "#{self.block_name} = #{scope_name}.$$p || nil"
406
+
407
+ unless @block_prepared
408
+ line "delete #{scope_name}.$$p;"
409
+ @block_prepared = true
410
+ end
411
+ end
412
+
374
413
  attr_accessor :await_encountered
375
414
  end
376
415
  end
@@ -13,14 +13,16 @@ module Opal
13
13
  push '(function(self, $parent_nesting) {'
14
14
 
15
15
  in_scope do
16
- add_temp '$nesting = [self].concat($parent_nesting)'
17
-
18
16
  body_stmt = stmt(compiler.returns(body))
17
+
18
+ add_temp '$nesting = [self].concat($parent_nesting)' if @define_nesting
19
+ add_temp '$$ = Opal.$r($nesting)' if @define_relative_access
20
+
19
21
  line scope.to_vars
20
22
  line body_stmt
21
23
  end
22
24
 
23
- line '})(Opal.get_singleton_class(', recv(object), '), $nesting)'
25
+ line '})(Opal.get_singleton_class(', recv(object), "), #{scope.nesting})"
24
26
  end
25
27
  end
26
28
  end
@@ -70,14 +70,14 @@ module Opal
70
70
 
71
71
  def super_method_invocation
72
72
  helper :find_super
73
- "$find_super(self, '#{method_id}', #{def_scope_identity}, #{defined_check_param}, #{allow_stubs})"
73
+ "$find_super(#{scope.self}, '#{method_id}', #{def_scope_identity}, #{defined_check_param}, #{allow_stubs})"
74
74
  end
75
75
 
76
76
  def super_block_invocation
77
77
  helper :find_block_super
78
78
  chain, cur_defn, mid = scope.super_chain
79
79
  trys = chain.map { |c| "#{c}.$$def" }.join(' || ')
80
- "$find_block_super(self, #{mid}, (#{trys} || #{cur_defn}), #{defined_check_param}, #{implicit_arguments_param})"
80
+ "$find_block_super(#{scope.self}, #{mid}, (#{trys} || #{cur_defn}), #{defined_check_param}, #{implicit_arguments_param})"
81
81
  end
82
82
 
83
83
  def compile_method_body
@@ -153,7 +153,7 @@ module Opal
153
153
  unless iter.type == :iter
154
154
  # Need to support passing block up even if it's not referenced in this method at all
155
155
  scope.uses_block!
156
- @iter = s(:js_tmp, '$iter')
156
+ @iter = s(:js_tmp, scope.block_name || '$yield')
157
157
  end
158
158
  end
159
159
 
@@ -179,28 +179,28 @@ module Opal
179
179
  same_arg_counter = Hash.new(0)
180
180
 
181
181
  def_scope.original_args.children.each do |sexp|
182
- arg_name = sexp.meta[:arg_name]
182
+ lvar_name = sexp.children[0]
183
183
 
184
184
  case sexp.type
185
185
  when :arg, :optarg
186
- arg_node = s(:lvar, arg_name)
186
+ arg_node = s(:lvar, lvar_name)
187
187
  # def m(_, _)
188
188
  # is compiled to
189
189
  # function $$m(_, __$2)
190
190
  # See Opal::Node::ArgsNode
191
- if arg_name[0] == '_'
192
- same_arg_counter[arg_name] += 1
193
- arg_node = s(:js_tmp, "#{arg_name}_$#{same_arg_counter[arg_name]}") if same_arg_counter[arg_name] > 1
191
+ if lvar_name[0] == '_'
192
+ same_arg_counter[lvar_name] += 1
193
+ arg_node = s(:js_tmp, "#{lvar_name}_$#{same_arg_counter[lvar_name]}") if same_arg_counter[lvar_name] > 1
194
194
  end
195
-
196
195
  args << arg_node
197
196
  when :restarg
198
- arg_node = arg_name ? s(:lvar, arg_name) : s(:js_tmp, '$rest_arg')
197
+ arg_node = lvar_name ? s(:lvar, lvar_name) : s(:js_tmp, '$rest_arg')
199
198
  args << s(:splat, arg_node)
200
199
  when :kwarg, :kwoptarg
201
- kwargs << s(:pair, s(:sym, arg_name), s(:lvar, arg_name))
200
+ key_name = sexp.meta[:arg_name]
201
+ kwargs << s(:pair, s(:sym, key_name), s(:lvar, lvar_name))
202
202
  when :kwrestarg
203
- arg_node = arg_name ? s(:lvar, arg_name) : s(:js_tmp, '$kw_rest_arg')
203
+ arg_node = lvar_name ? s(:lvar, lvar_name) : s(:js_tmp, '$kw_rest_arg')
204
204
  kwargs << s(:kwsplat, arg_node)
205
205
  end
206
206
  end