opal 1.5.1 → 1.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +4 -0
  3. data/.github/workflows/build.yml +17 -3
  4. data/HACKING.md +23 -0
  5. data/README.md +3 -3
  6. data/UNRELEASED.md +47 -0
  7. data/benchmark/run.rb +1 -0
  8. data/docs/compiled_ruby.md +8 -0
  9. data/docs/compiler.md +1 -1
  10. data/docs/compiler_directives.md +1 -1
  11. data/docs/getting_started.md +17 -0
  12. data/docs/headless_chrome.md +1 -1
  13. data/docs/index.md +123 -0
  14. data/docs/jquery.md +5 -5
  15. data/docs/templates.md +37 -37
  16. data/docs/unsupported_features.md +0 -4
  17. data/lib/opal/builder.rb +59 -39
  18. data/lib/opal/builder_processors.rb +24 -0
  19. data/lib/opal/builder_scheduler/prefork.rb +262 -0
  20. data/lib/opal/builder_scheduler/sequential.rb +13 -0
  21. data/lib/opal/builder_scheduler.rb +29 -0
  22. data/lib/opal/cache/file_cache.rb +13 -2
  23. data/lib/opal/cli.rb +36 -19
  24. data/lib/opal/cli_options.rb +4 -0
  25. data/lib/opal/cli_runners/chrome.rb +17 -13
  26. data/lib/opal/cli_runners/chrome_cdp_interface.rb +19 -2
  27. data/lib/opal/cli_runners/compiler.rb +1 -1
  28. data/lib/opal/cli_runners/gjs.rb +3 -1
  29. data/lib/opal/cli_runners/mini_racer.rb +5 -3
  30. data/lib/opal/cli_runners/nodejs.rb +3 -3
  31. data/lib/opal/cli_runners/server.rb +13 -28
  32. data/lib/opal/cli_runners/system_runner.rb +5 -3
  33. data/lib/opal/cli_runners.rb +7 -6
  34. data/lib/opal/compiler.rb +25 -2
  35. data/lib/opal/config.rb +10 -0
  36. data/lib/opal/eof_content.rb +5 -2
  37. data/lib/opal/nodes/args/ensure_kwargs_are_kwargs.rb +2 -6
  38. data/lib/opal/nodes/args/extract_kwarg.rb +3 -4
  39. data/lib/opal/nodes/args/extract_kwargs.rb +3 -1
  40. data/lib/opal/nodes/args/extract_kwoptarg.rb +1 -1
  41. data/lib/opal/nodes/args/extract_kwrestarg.rb +4 -1
  42. data/lib/opal/nodes/args/extract_optarg.rb +1 -1
  43. data/lib/opal/nodes/args/extract_post_arg.rb +1 -1
  44. data/lib/opal/nodes/args/extract_post_optarg.rb +1 -1
  45. data/lib/opal/nodes/args/extract_restarg.rb +2 -2
  46. data/lib/opal/nodes/args/initialize_iterarg.rb +1 -1
  47. data/lib/opal/nodes/args/initialize_shadowarg.rb +1 -1
  48. data/lib/opal/nodes/args/prepare_post_args.rb +4 -2
  49. data/lib/opal/nodes/base.rb +14 -3
  50. data/lib/opal/nodes/call.rb +13 -16
  51. data/lib/opal/nodes/class.rb +3 -1
  52. data/lib/opal/nodes/closure.rb +250 -0
  53. data/lib/opal/nodes/def.rb +7 -11
  54. data/lib/opal/nodes/definitions.rb +4 -2
  55. data/lib/opal/nodes/if.rb +12 -2
  56. data/lib/opal/nodes/iter.rb +11 -17
  57. data/lib/opal/nodes/logic.rb +15 -63
  58. data/lib/opal/nodes/module.rb +3 -1
  59. data/lib/opal/nodes/rescue.rb +23 -15
  60. data/lib/opal/nodes/scope.rb +7 -1
  61. data/lib/opal/nodes/top.rb +27 -4
  62. data/lib/opal/nodes/while.rb +42 -26
  63. data/lib/opal/nodes.rb +1 -0
  64. data/lib/opal/os.rb +59 -0
  65. data/lib/opal/rewriter.rb +2 -0
  66. data/lib/opal/rewriters/returnable_logic.rb +14 -0
  67. data/lib/opal/rewriters/thrower_finder.rb +90 -0
  68. data/lib/opal/simple_server.rb +12 -6
  69. data/lib/opal/source_map/file.rb +4 -3
  70. data/lib/opal/source_map/map.rb +9 -1
  71. data/lib/opal/util.rb +1 -1
  72. data/lib/opal/version.rb +1 -1
  73. data/opal/corelib/array.rb +68 -3
  74. data/opal/corelib/basic_object.rb +1 -0
  75. data/opal/corelib/comparable.rb +1 -1
  76. data/opal/corelib/complex.rb +1 -0
  77. data/opal/corelib/constants.rb +2 -2
  78. data/opal/corelib/enumerable.rb +4 -2
  79. data/opal/corelib/enumerator/chain.rb +4 -0
  80. data/opal/corelib/enumerator/generator.rb +5 -3
  81. data/opal/corelib/enumerator/lazy.rb +3 -1
  82. data/opal/corelib/enumerator/yielder.rb +2 -4
  83. data/opal/corelib/enumerator.rb +3 -1
  84. data/opal/corelib/error/errno.rb +3 -1
  85. data/opal/corelib/error.rb +13 -2
  86. data/opal/corelib/hash.rb +39 -1
  87. data/opal/corelib/io.rb +1 -1
  88. data/opal/corelib/kernel.rb +56 -5
  89. data/opal/corelib/module.rb +60 -4
  90. data/opal/corelib/proc.rb +8 -5
  91. data/opal/corelib/rational.rb +1 -0
  92. data/opal/corelib/regexp.rb +15 -1
  93. data/opal/corelib/runtime.js +307 -238
  94. data/opal/corelib/string/encoding.rb +0 -6
  95. data/opal/corelib/string.rb +28 -7
  96. data/opal/corelib/time.rb +5 -2
  97. data/opal/corelib/unsupported.rb +2 -14
  98. data/opal.gemspec +2 -2
  99. data/spec/filters/bugs/delegate.rb +11 -0
  100. data/spec/filters/bugs/kernel.rb +1 -3
  101. data/spec/filters/bugs/language.rb +3 -23
  102. data/spec/filters/bugs/method.rb +0 -1
  103. data/spec/filters/bugs/module.rb +0 -3
  104. data/spec/filters/bugs/proc.rb +0 -3
  105. data/spec/filters/bugs/set.rb +4 -16
  106. data/spec/filters/bugs/unboundmethod.rb +0 -2
  107. data/spec/filters/unsupported/array.rb +0 -58
  108. data/spec/filters/unsupported/freeze.rb +8 -192
  109. data/spec/filters/unsupported/hash.rb +0 -25
  110. data/spec/filters/unsupported/kernel.rb +0 -1
  111. data/spec/filters/unsupported/privacy.rb +17 -0
  112. data/spec/lib/builder_spec.rb +14 -0
  113. data/spec/lib/cli_runners/server_spec.rb +2 -3
  114. data/spec/lib/cli_spec.rb +15 -1
  115. data/spec/lib/compiler_spec.rb +1 -1
  116. data/spec/opal/core/language/DATA/characters_support_crlf_spec.rb +9 -0
  117. data/spec/opal/core/language/DATA/multiple___END___crlf_spec.rb +10 -0
  118. data/spec/opal/core/language/if_spec.rb +13 -0
  119. data/spec/opal/core/language/safe_navigator_spec.rb +10 -0
  120. data/spec/opal/core/module_spec.rb +8 -0
  121. data/spec/ruby_specs +2 -1
  122. data/stdlib/await.rb +44 -7
  123. data/stdlib/delegate.rb +427 -6
  124. data/stdlib/headless_chrome.rb +6 -2
  125. data/stdlib/nodejs/file.rb +2 -1
  126. data/stdlib/opal-parser.rb +1 -1
  127. data/stdlib/opal-platform.rb +1 -1
  128. data/stdlib/opal-replutils.rb +5 -3
  129. data/stdlib/promise.rb +3 -0
  130. data/stdlib/rbconfig.rb +4 -1
  131. data/stdlib/ruby2_keywords.rb +60 -0
  132. data/stdlib/set.rb +21 -0
  133. data/tasks/performance.rake +41 -35
  134. data/tasks/releasing.rake +1 -0
  135. data/tasks/testing/mspec_special_calls.rb +1 -0
  136. data/tasks/testing.rake +13 -12
  137. data/test/nodejs/test_await.rb +39 -1
  138. data/test/nodejs/test_file.rb +3 -2
  139. metadata +37 -22
  140. data/docs/faq.md +0 -17
  141. data/lib/opal/rewriters/break_finder.rb +0 -36
  142. data/spec/opal/core/kernel/freeze_spec.rb +0 -15
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Opal
4
+ module Nodes
5
+ # This module takes care of providing information about the
6
+ # closure stack that we have for the nodes during compile time.
7
+ # This is not a typical node.
8
+ #
9
+ # Also, while loops are not closures per se, this module also
10
+ # takes a note about them.
11
+ #
12
+ # Then we can use this information for control flow like
13
+ # generating breaks, nexts, returns.
14
+ class Closure
15
+ NONE = 0
16
+
17
+ @types = {}
18
+
19
+ def self.add_type(name, value)
20
+ const_set(name, value)
21
+ @types[name] = value
22
+ end
23
+
24
+ def self.type_inspect(type)
25
+ @types.reject do |_name, value|
26
+ (type & value) == 0
27
+ end.map(&:first).join("|")
28
+ end
29
+
30
+ add_type(:JS_FUNCTION, 1 << 0) # everything that generates an IIFE
31
+ add_type(:JS_LOOP, 1 << 1) # exerything that generates a JS loop
32
+ add_type(:JS_LOOP_INSIDE, 1 << 2) # everything that generates an inside of a loop
33
+
34
+ add_type(:DEF, 1 << 3) # def
35
+ add_type(:LAMBDA, 1 << 4) # lambda
36
+ add_type(:ITER, 1 << 5) # iter, lambda
37
+ add_type(:MODULE, 1 << 6)
38
+ add_type(:LOOP, 1 << 7) # for building a catcher outside a loop
39
+ add_type(:LOOP_INSIDE, 1 << 8) # for building a catcher inside a loop
40
+ add_type(:SEND, 1 << 9) # to generate a break catcher after send with a block
41
+ add_type(:TOP, 1 << 10)
42
+ add_type(:RESCUE_RETRIER, 1 << 11) # a virtual loop to catch a retrier
43
+
44
+ ANY = 0xffffffff
45
+
46
+ def initialize(node, type, parent)
47
+ @node, @type, @parent = node, type, parent
48
+ @catchers = []
49
+ @throwers = {}
50
+ end
51
+
52
+ def register_catcher(type = :return)
53
+ @catchers << type unless @catchers.include? type
54
+
55
+ "$t_#{type}"
56
+ end
57
+
58
+ def register_thrower(type, id)
59
+ @throwers[type] = id
60
+ end
61
+
62
+ def is?(type)
63
+ (@type & type) != 0
64
+ end
65
+
66
+ def inspect
67
+ "#<Closure #{Closure.type_inspect(type)} #{@node.class}>"
68
+ end
69
+
70
+ attr_accessor :node, :type, :parent, :catchers, :throwers
71
+
72
+ module NodeSupport
73
+ def push_closure(type = JS_FUNCTION)
74
+ closure = Closure.new(self, type, select_closure)
75
+ @compiler.closure_stack << closure
76
+ @closure = closure
77
+ end
78
+
79
+ attr_accessor :closure
80
+
81
+ def pop_closure
82
+ compile_catcher
83
+ @compiler.closure_stack.pop
84
+ last = @compiler.closure_stack.last
85
+ @closure = last if last&.node == self
86
+ end
87
+
88
+ def in_closure(type = JS_FUNCTION)
89
+ closure = push_closure(type)
90
+ out = yield closure
91
+ pop_closure
92
+ out
93
+ end
94
+
95
+ def select_closure(type = ANY, break_after: NONE)
96
+ @compiler.closure_stack.reverse.find do |i|
97
+ break if (i.type & break_after) != 0
98
+ (i.type & type) != 0
99
+ end
100
+ end
101
+
102
+ def generate_thrower(type, closure, value)
103
+ id = closure.register_catcher(type)
104
+ closure.register_thrower(type, id)
105
+ push id, '.$throw(', expr_or_empty(value), ')'
106
+ id
107
+ end
108
+
109
+ def generate_thrower_without_catcher(type, closure, value)
110
+ helper :thrower
111
+
112
+ if closure.throwers.key? type
113
+ id = closure.throwers[type]
114
+ else
115
+ id = compiler.unique_temp('t_')
116
+ scope = closure.node.scope&.parent || top_scope
117
+ scope.add_scope_temp("#{id} = $thrower('#{type}')")
118
+ closure.register_thrower(type, id)
119
+ end
120
+ push id, '.$throw(', expr_or_empty(value), ')'
121
+ id
122
+ end
123
+
124
+ def thrower(type, value = nil)
125
+ case type
126
+ when :return
127
+ thrower_closure = select_closure(DEF, break_after: MODULE | TOP)
128
+ last_closure = select_closure(JS_FUNCTION)
129
+
130
+ if !thrower_closure
131
+ iter_closure = select_closure(ITER, break_after: DEF | MODULE | TOP)
132
+ if iter_closure
133
+ generate_thrower_without_catcher(:return, iter_closure, value)
134
+ elsif compiler.eval?
135
+ push 'Opal.t_eval_return.$throw(', expr_or_empty(value), ')'
136
+ else
137
+ error 'Invalid return'
138
+ end
139
+ elsif thrower_closure == last_closure
140
+ push 'return ', expr_or_nil(value)
141
+ else
142
+ id = generate_thrower(:return, thrower_closure, value)
143
+ # Additionally, register our thrower on the surrounding iter, if present
144
+ iter_closure = select_closure(ITER, break_after: DEF | MODULE | TOP)
145
+ iter_closure.register_thrower(:return, id) if iter_closure
146
+ end
147
+ when :eval_return
148
+ thrower_closure = select_closure(DEF | LAMBDA, break_after: MODULE | TOP)
149
+
150
+ if thrower_closure
151
+ thrower_closure.register_catcher(:eval_return)
152
+ end
153
+ when :next, :redo
154
+ thrower_closure = select_closure(ITER | LOOP_INSIDE, break_after: DEF | MODULE | TOP)
155
+ last_closure = select_closure(JS_FUNCTION | JS_LOOP_INSIDE)
156
+
157
+ if !thrower_closure
158
+ error 'Invalid next'
159
+ elsif thrower_closure == last_closure
160
+ if thrower_closure.is? LOOP_INSIDE
161
+ push 'continue'
162
+ elsif thrower_closure.is? ITER | LAMBDA
163
+ push 'return ', expr_or_nil(value)
164
+ end
165
+ else
166
+ generate_thrower(:next, thrower_closure, value)
167
+ end
168
+ when :break
169
+ thrower_closure = select_closure(SEND | LAMBDA | LOOP, break_after: DEF | MODULE | TOP)
170
+ last_closure = select_closure(JS_FUNCTION | JS_LOOP)
171
+
172
+ if !thrower_closure
173
+ iter_closure = select_closure(ITER, break_after: DEF | MODULE | TOP)
174
+ if iter_closure
175
+ generate_thrower_without_catcher(:break, iter_closure, value)
176
+ else
177
+ error 'Invalid break'
178
+ end
179
+ elsif thrower_closure == last_closure
180
+ if thrower_closure.is? JS_FUNCTION | LAMBDA
181
+ push 'return ', expr_or_nil(value)
182
+ elsif thrower_closure.is? LOOP
183
+ push 'break'
184
+ end
185
+ else
186
+ generate_thrower(:break, thrower_closure, value)
187
+ end
188
+ when :retry
189
+ thrower_closure = select_closure(RESCUE_RETRIER, break_after: DEF | MODULE | TOP)
190
+ last_closure = select_closure(JS_LOOP_INSIDE)
191
+
192
+ if !thrower_closure
193
+ error 'Invalid retry'
194
+ elsif thrower_closure == last_closure
195
+ push 'continue'
196
+ else
197
+ generate_thrower(:retry, thrower_closure, value)
198
+ end
199
+ end
200
+ end
201
+
202
+ def closure_is?(type)
203
+ @closure.is?(type)
204
+ end
205
+
206
+ # Generate a catcher if thrower has been used
207
+ def compile_catcher
208
+ catchers = @closure.catchers
209
+
210
+ return if catchers.empty?
211
+
212
+ helper :thrower
213
+
214
+ push "} catch($e) {"
215
+ indent do
216
+ @closure.catchers.each do |type|
217
+ case type
218
+ when :eval_return
219
+ line "if ($e === Opal.t_eval_return) return $e.$v;"
220
+ else
221
+ line "if ($e === $t_#{type}) return $e.$v;"
222
+ end
223
+ end
224
+ line "throw $e;"
225
+ end
226
+ line "}"
227
+
228
+ unshift "return " if closure_is? SEND
229
+
230
+ unshift "var ", catchers.map { |type| "$t_#{type} = $thrower('#{type}')" }.join(", "), "; "
231
+ unshift "try { "
232
+
233
+ unless closure_is? JS_FUNCTION
234
+ if scope.await_encountered
235
+ wrap "(await (async function(){", "})())"
236
+ else
237
+ wrap "(function(){", "})()"
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ module CompilerSupport
244
+ def closure_stack
245
+ @closure_stack ||= []
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -56,22 +56,18 @@ module Opal
56
56
 
57
57
  inline_params = process(inline_args)
58
58
 
59
- stmt_code = stmt(compiler.returns(stmts))
59
+ in_closure(Closure::DEF | Closure::JS_FUNCTION) do
60
+ stmt_code = stmt(compiler.returns(stmts))
60
61
 
61
- compile_block_arg
62
+ compile_block_arg
62
63
 
63
- add_temp 'self = this' if @define_self
64
+ add_temp 'self = this' if @define_self
64
65
 
65
- compile_arity_check
66
+ compile_arity_check
66
67
 
67
- unshift "\n#{current_indent}", scope.to_vars
68
+ unshift "\n#{current_indent}", scope.to_vars
68
69
 
69
- line stmt_code
70
-
71
- if scope.catch_return
72
- unshift "try {\n"
73
- line '} catch ($returner) { if ($returner === Opal.returner) { return $returner.$v }'
74
- push ' throw $returner; }'
70
+ line stmt_code
75
71
  end
76
72
  end
77
73
 
@@ -54,9 +54,11 @@ module Opal
54
54
  elsif children.size == 1
55
55
  compile_inline_children(returned_children, @level)
56
56
  else
57
- compile_children(returned_children, @level)
57
+ in_closure do
58
+ compile_children(returned_children, @level)
59
+ end
58
60
 
59
- if scope.parent.await_encountered
61
+ if scope.parent&.await_encountered
60
62
  wrap '(await (async function() {', '})())'
61
63
  else
62
64
  wrap '(function() {', '})()'
data/lib/opal/nodes/if.rb CHANGED
@@ -27,6 +27,8 @@ module Opal
27
27
  end
28
28
 
29
29
  def compile_with_if
30
+ push_closure if expects_expression?
31
+
30
32
  truthy = self.truthy
31
33
  falsy = self.falsy
32
34
 
@@ -60,15 +62,23 @@ module Opal
60
62
  line 'return nil;' if expects_expression?
61
63
  end
62
64
 
65
+ pop_closure if expects_expression?
66
+
63
67
  if expects_expression?
68
+ return_kw = 'return ' if returning_if?
69
+
64
70
  if scope.await_encountered
65
- wrap '(await (async function() {', '})())'
71
+ wrap "#{return_kw}(await (async function() {", '})())'
66
72
  else
67
- wrap '(function() {', '})()'
73
+ wrap "#{return_kw}(function() {", '})()'
68
74
  end
69
75
  end
70
76
  end
71
77
 
78
+ def returning_if?
79
+ @sexp.meta[:returning]
80
+ end
81
+
72
82
  def truthy
73
83
  returnify(true_body)
74
84
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'opal/nodes/node_with_args'
4
- require 'opal/rewriters/break_finder'
5
4
 
6
5
  module Opal
7
6
  module Nodes
@@ -18,12 +17,17 @@ module Opal
18
17
  blockopts = []
19
18
  blockopts << "$$arity: #{arity}"
20
19
  blockopts << "$$s: #{scope.self}" if @define_self
21
- blockopts << "$$brk: $brk" if contains_break?
20
+ blockopts << "$$brk: #{@closure.throwers[:break]}" if @closure&.throwers&.key? :break
21
+ blockopts << "$$ret: #{@closure.throwers[:return]}" if @closure&.throwers&.key? :return
22
22
 
23
23
  if compiler.arity_check?
24
24
  blockopts << "$$parameters: #{parameters_code}"
25
25
  end
26
26
 
27
+ if compiler.enable_source_location?
28
+ blockopts << "$$source_location: #{source_location}"
29
+ end
30
+
27
31
  # MRI expands a passed argument if the block:
28
32
  # 1. takes a single argument that is an array
29
33
  # 2. has more that one argument
@@ -62,18 +66,14 @@ module Opal
62
66
 
63
67
  compile_arity_check
64
68
 
65
- body_code = stmt(returned_body)
66
-
67
- add_temp "self = #{identity}.$$s == null ? this : #{identity}.$$s" if @define_self
69
+ in_closure(Closure::JS_FUNCTION | Closure::ITER | (@is_lambda ? Closure::LAMBDA : 0)) do
70
+ body_code = stmt(returned_body)
68
71
 
69
- to_vars = scope.to_vars
72
+ add_temp "self = #{identity}.$$s == null ? this : #{identity}.$$s" if @define_self
70
73
 
71
- line body_code
74
+ to_vars = scope.to_vars
72
75
 
73
- if scope.catch_return
74
- unshift "try {\n"
75
- line '} catch ($returner) { if ($returner === Opal.returner) { return $returner.$v }'
76
- push ' throw $returner; }'
76
+ line body_code
77
77
  end
78
78
  end
79
79
 
@@ -135,12 +135,6 @@ module Opal
135
135
  def arity_check_node
136
136
  s(:iter_arity_check, original_args)
137
137
  end
138
-
139
- def contains_break?
140
- finder = Opal::Rewriters::BreakFinder.new
141
- finder.process(@sexp)
142
- finder.found_break?
143
- end
144
138
  end
145
139
  end
146
140
  end
@@ -8,13 +8,7 @@ module Opal
8
8
  handle :next
9
9
 
10
10
  def compile
11
- if in_while?
12
- push 'continue;'
13
- elsif scope.iter?
14
- push 'return ', expr_or_nil(value), ';'
15
- else
16
- error 'Invalid next'
17
- end
11
+ thrower(:next, value)
18
12
  end
19
13
 
20
14
  def value
@@ -35,35 +29,7 @@ module Opal
35
29
  children :value
36
30
 
37
31
  def compile
38
- if in_while?
39
- compile_while
40
- elsif scope.iter?
41
- compile_iter
42
- else
43
- error 'void value expression: cannot use break outside of iter/while'
44
- end
45
- end
46
-
47
- def compile_while
48
- if while_loop[:closure]
49
- push 'return ', expr_or_nil(value)
50
- else
51
- push 'break;'
52
- end
53
- end
54
-
55
- def compile_iter
56
- error 'break must be used as a statement' unless stmt?
57
-
58
- line 'Opal.brk(', break_val, ', $brk)'
59
- end
60
-
61
- def break_val
62
- if value.nil?
63
- expr(s(:nil))
64
- else
65
- expr(value)
66
- end
32
+ thrower(:break, value)
67
33
  end
68
34
  end
69
35
 
@@ -81,8 +47,8 @@ module Opal
81
47
  end
82
48
 
83
49
  def compile_while
84
- while_loop[:use_redo] = true
85
- push "#{while_loop[:redo_var]} = true; continue;"
50
+ push "#{while_loop[:redo_var]} = true;"
51
+ thrower(:redo)
86
52
  end
87
53
 
88
54
  def compile_iter
@@ -110,6 +76,13 @@ module Opal
110
76
  end
111
77
  end
112
78
 
79
+ class RetryNode < Base
80
+ handle :retry
81
+
82
+ def compile
83
+ thrower(:retry)
84
+ end
85
+ end
113
86
 
114
87
  class ReturnNode < Base
115
88
  handle :return
@@ -118,37 +91,16 @@ module Opal
118
91
 
119
92
  def return_val
120
93
  if value.nil?
121
- expr(s(:nil))
94
+ s(:nil)
122
95
  elsif children.size > 1
123
- expr(s(:array, *children))
96
+ s(:array, *children)
124
97
  else
125
- expr(value)
98
+ value
126
99
  end
127
100
  end
128
101
 
129
- def return_in_iter?
130
- if (scope.iter? && !scope.lambda?) && parent_def = scope.find_parent_def
131
- parent_def
132
- end
133
- end
134
-
135
- def return_expr_in_def?
136
- return scope if expr? && (scope.def? || scope.lambda?)
137
- end
138
-
139
- def scope_to_catch_return
140
- return_in_iter? || return_expr_in_def?
141
- end
142
-
143
102
  def compile
144
- if def_scope = scope_to_catch_return
145
- def_scope.catch_return = true
146
- push 'Opal.ret(', return_val, ')'
147
- elsif stmt?
148
- push 'return ', return_val
149
- else
150
- error 'void value expression: cannot return as an expression'
151
- end
103
+ thrower(:return, return_val)
152
104
  end
153
105
  end
154
106
 
@@ -24,7 +24,9 @@ module Opal
24
24
  line " var self = $module($base, '#{name}');"
25
25
  in_scope do
26
26
  scope.name = name
27
- compile_body
27
+ in_closure(Closure::MODULE | Closure::JS_FUNCTION) do
28
+ compile_body
29
+ end
28
30
  end
29
31
 
30
32
  if await_encountered
@@ -10,6 +10,8 @@ module Opal
10
10
  children :begn, :ensr
11
11
 
12
12
  def compile
13
+ push_closure if wrap_in_closure?
14
+
13
15
  push 'try {'
14
16
 
15
17
  in_ensure do
@@ -49,6 +51,8 @@ module Opal
49
51
 
50
52
  line '}'
51
53
 
54
+ pop_closure if wrap_in_closure?
55
+
52
56
  if wrap_in_closure?
53
57
  if scope.await_encountered
54
58
  wrap '(await (async function() { ', '; })())'
@@ -79,6 +83,10 @@ module Opal
79
83
  rescue_else_code = compiler.returns(rescue_else_code) unless stmt?
80
84
  rescue_else_code
81
85
  end
86
+
87
+ def has_rescue_else?
88
+ @sexp.meta[:has_rescue_else]
89
+ end
82
90
  end
83
91
 
84
92
  class RescueNode < Base
@@ -94,6 +102,15 @@ module Opal
94
102
  line 'var $no_errors = true;'
95
103
  end
96
104
 
105
+ closure_type = Closure::NONE
106
+ closure_type |= Closure::JS_FUNCTION if expr? || recv?
107
+ if has_retry?
108
+ closure_type |= Closure::JS_LOOP \
109
+ | Closure::JS_LOOP_INSIDE \
110
+ | Closure::RESCUE_RETRIER
111
+ end
112
+ push_closure(closure_type) if closure_type != Closure::NONE
113
+
97
114
  in_rescue(self) do
98
115
  push 'try {'
99
116
  indent do
@@ -133,10 +150,12 @@ module Opal
133
150
  end
134
151
  push '}'
135
152
  end
136
-
137
- wrap "#{retry_id}: do { ", ' break; } while(1)' if retry_id
138
153
  end
139
154
 
155
+ pop_closure if closure_type != Closure::NONE
156
+
157
+ wrap 'do { ', ' break; } while(1)' if has_retry?
158
+
140
159
  # Wrap a try{} catch{} into a function
141
160
  # when it's an expression
142
161
  # or when there's a method call after begin;rescue;end
@@ -168,11 +187,9 @@ module Opal
168
187
  !in_ensure? && has_rescue_else?
169
188
  end
170
189
 
171
- def gen_retry_id
172
- @retry_id ||= scope.gen_retry_id
190
+ def has_retry?
191
+ @sexp.meta[:has_retry]
173
192
  end
174
-
175
- attr_reader :retry_id
176
193
  end
177
194
 
178
195
  class ResBodyNode < Base
@@ -209,14 +226,5 @@ module Opal
209
226
  body_code
210
227
  end
211
228
  end
212
-
213
- class RetryNode < Base
214
- handle :retry
215
-
216
- def compile
217
- error 'Invalid retry' unless in_resbody?
218
- push "continue #{scope.current_rescue.gen_retry_id}"
219
- end
220
- end
221
229
  end
222
230
  end
@@ -197,6 +197,12 @@ module Opal
197
197
  @temps.push(tmp)
198
198
  end
199
199
 
200
+ def prepend_scope_temp(tmp)
201
+ return if has_temp?(tmp)
202
+
203
+ @temps.unshift(tmp)
204
+ end
205
+
200
206
  def has_temp?(tmp)
201
207
  @temps.include? tmp
202
208
  end
@@ -405,7 +411,7 @@ module Opal
405
411
  add_temp "#{self.block_name} = #{scope_name}.$$p || nil"
406
412
 
407
413
  unless @block_prepared
408
- line "delete #{scope_name}.$$p;"
414
+ line "#{scope_name}.$$p = null;"
409
415
  @block_prepared = true
410
416
  end
411
417
  end