opal 1.4.1 → 1.5.0.rc1

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +5 -3
  3. data/.rubocop.yml +1 -0
  4. data/UNRELEASED.md +37 -2
  5. data/benchmark-ips/bm_js_symbols_vs_strings.rb +39 -14
  6. data/docs/releasing.md +10 -2
  7. data/lib/opal/ast/matcher.rb +77 -0
  8. data/lib/opal/cache.rb +1 -1
  9. data/lib/opal/cli_runners/applescript.rb +2 -0
  10. data/lib/opal/compiler.rb +18 -9
  11. data/lib/opal/nodes/call.rb +73 -28
  12. data/lib/opal/nodes/def.rb +31 -27
  13. data/lib/opal/nodes/definitions.rb +2 -0
  14. data/lib/opal/nodes/helpers.rb +4 -23
  15. data/lib/opal/nodes/if.rb +222 -0
  16. data/lib/opal/nodes/iter.rb +41 -37
  17. data/lib/opal/nodes/literal.rb +2 -2
  18. data/lib/opal/nodes/masgn.rb +15 -17
  19. data/lib/opal/nodes/node_with_args/shortcuts.rb +100 -0
  20. data/lib/opal/nodes/node_with_args.rb +1 -0
  21. data/lib/opal/nodes/top.rb +26 -10
  22. data/lib/opal/nodes.rb +0 -1
  23. data/lib/opal/parser/default_config.rb +3 -2
  24. data/lib/opal/repl.rb +1 -1
  25. data/lib/opal/rewriter.rb +13 -6
  26. data/lib/opal/rewriters/base.rb +12 -1
  27. data/lib/opal/rewriters/rubyspec/filters_rewriter.rb +1 -0
  28. data/lib/opal/version.rb +1 -1
  29. data/opal/corelib/array.rb +23 -28
  30. data/opal/corelib/binding.rb +14 -4
  31. data/opal/corelib/constants.rb +3 -3
  32. data/opal/corelib/hash.rb +2 -2
  33. data/opal/corelib/irb.rb +192 -0
  34. data/opal/corelib/math/polyfills.rb +127 -0
  35. data/opal/corelib/math.rb +14 -194
  36. data/opal/corelib/module.rb +23 -25
  37. data/opal/corelib/number.rb +63 -14
  38. data/opal/corelib/regexp.rb +2 -0
  39. data/opal/corelib/runtime.js +56 -20
  40. data/opal/corelib/string.rb +38 -59
  41. data/opal/corelib/time.rb +106 -68
  42. data/opal/opal/full.rb +0 -1
  43. data/opal/opal.rb +4 -1
  44. data/spec/filters/bugs/date.rb +0 -3
  45. data/spec/filters/bugs/datetime.rb +65 -0
  46. data/spec/filters/bugs/float.rb +0 -18
  47. data/spec/filters/bugs/hash.rb +0 -2
  48. data/spec/filters/bugs/language.rb +0 -3
  49. data/spec/filters/bugs/marshal.rb +0 -1
  50. data/spec/filters/bugs/string.rb +0 -30
  51. data/spec/filters/bugs/time.rb +18 -8
  52. data/spec/lib/cli_spec.rb +2 -2
  53. data/spec/lib/compiler_spec.rb +8 -8
  54. data/spec/lib/rewriters/base_spec.rb +1 -1
  55. data/spec/lib/rewriters/binary_operator_assignment_spec.rb +34 -59
  56. data/spec/lib/rewriters/block_to_iter_spec.rb +3 -6
  57. data/spec/lib/rewriters/dot_js_syntax_spec.rb +2 -5
  58. data/spec/lib/rewriters/for_rewriter_spec.rb +0 -1
  59. data/spec/lib/rewriters/forward_args_spec.rb +2 -3
  60. data/spec/lib/rewriters/js_reserved_words_spec.rb +2 -15
  61. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +64 -89
  62. data/spec/lib/rewriters/numblocks_spec.rb +3 -5
  63. data/spec/lib/rewriters/opal_engine_check_spec.rb +2 -14
  64. data/spec/lib/rewriters/rubyspec/filters_rewriter_spec.rb +10 -2
  65. data/spec/opal/compiler/irb_spec.rb +4 -0
  66. data/spec/opal/core/language/super_spec.rb +26 -0
  67. data/spec/opal/core/regexp/assertions_spec.rb +19 -0
  68. data/spec/opal/core/string/to_proc_spec.rb +19 -0
  69. data/spec/ruby_specs +4 -0
  70. data/spec/support/rewriters_helper.rb +43 -23
  71. data/stdlib/date/date_time.rb +71 -0
  72. data/stdlib/date/formatters.rb +28 -0
  73. data/stdlib/date/infinity.rb +73 -0
  74. data/stdlib/date.rb +77 -214
  75. data/stdlib/opal/repl_js.rb +1 -1
  76. data/stdlib/{opal/replutils.rb → opal-replutils.rb} +3 -3
  77. data/stdlib/time.rb +39 -2
  78. data/stdlib/uri.rb +53 -0
  79. data/tasks/performance/asciidoctor_test.rb.erb +3 -1
  80. data/tasks/performance/optimization_status.rb +3 -2
  81. data/tasks/performance.rake +69 -35
  82. data/tasks/testing.rake +1 -0
  83. data/test/opal/test_uri.rb +35 -0
  84. data/yarn.lock +27 -5
  85. metadata +31 -18
  86. data/lib/opal/nodes/csend.rb +0 -24
  87. data/lib/opal/rewriters/explicit_writer_return.rb +0 -59
  88. data/spec/lib/rewriters/explicit_writer_return_spec.rb +0 -186
  89. data/stdlib/nodejs/irb.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f199262b85612cb61bd3f3fa1d05ec8a7fb3fa6fabbd63a36baa03859e241c5c
4
- data.tar.gz: e4af60acb540708612fbed3e4aa04fef968b750b0ff588774bda6d979586fc8f
3
+ metadata.gz: 6ac3c5ae91f1f31b94c292f0aed320bdba76511abd34bb6dc45294c11958e802
4
+ data.tar.gz: 473ca0893f427b05c88c64805dc0f64668816b263b136863b903f5262aba7aef
5
5
  SHA512:
6
- metadata.gz: a4ece0468468bc541c155cb53185550f35b4277c89e18f32990a7bb8abff6c20beaeb9f962f02f80b1afc251a75c0916d35dd8d2944fa8153aa4a6454a7ddf8a
7
- data.tar.gz: 81539b2993e44bfa8217cda753badaa292c26117de8366d04e50922309b95654f5c09f983a02f329b36ea1405affc52c3831c7122f4215a90e7a78c9084b9b31
6
+ metadata.gz: 5ba1e5223c56695db351ede10b36872590b29701f4576c9a1962502686434548d855d71fdd147e5b34492bb70964db72bed3229cc552dfa139a2db747dd4caa1
7
+ data.tar.gz: 30e97d6c8293b918084145f15cd9f42d6495989405d97ebbccc414440c281a8297405f651560e8a65ea21dda191631d9bd20fda058d5693af1c25b8b61cc1e40
data/.eslintrc.js CHANGED
@@ -24,12 +24,14 @@ module.exports = {
24
24
  "no-control-regex": "off",
25
25
  },
26
26
  "globals": {
27
- "Opal": "readonly",
28
- "DataView": "readonly",
29
27
  "ArrayBuffer": "readonly",
28
+ "DataView": "readonly",
30
29
  "globalThis": "readonly",
31
- "Uint8Array": "readonly",
30
+ "Opal": "readonly",
32
31
  "Promise": "readonly",
32
+ "Proxy": "readonly",
33
+ "Reflect": "readonly",
34
+ "Uint8Array": "readonly",
33
35
  "WeakRef": "readonly",
34
36
  }
35
37
  };
data/.rubocop.yml CHANGED
@@ -365,6 +365,7 @@ Naming/FileName:
365
365
  - 'stdlib/opal-parser.rb'
366
366
  - 'stdlib/opal-platform.rb'
367
367
  - 'stdlib/opal-source-maps.rb'
368
+ - 'stdlib/opal-replutils.rb'
368
369
 
369
370
  Naming/ConstantName:
370
371
  Exclude:
data/UNRELEASED.md CHANGED
@@ -1,8 +1,43 @@
1
- <!--
1
+ ### Added
2
+
3
+ - Introduce timezone support for Time (#2394)
4
+ - DateTime and Date refactor (#2398)
5
+ - Implement `Number#prev_float`/`#next_float` (#2404)
6
+ - Support `binding.irb` anywhere in the code, for both browsers and node (#2392)
7
+ - Added `URI.decode_www_form` (#2387)
8
+
9
+ ### Changed
10
+
11
+ - Move Math IE11-supporting polyfills to a separate file (#2395)
12
+ - String methods always return Strings even when overloaded (#2413)
13
+ - `alias` calls will not add the "old name" method to the list of stubs for method missing (#2414)
14
+ - Optimize writer/setter methods, up to 4% performance gain (#2402)
15
+
16
+ ### Performance
17
+
18
+ - Improve performance of argument coertion, fast-track `Integer`, `String`, and calling the designed coertion method (#2383)
19
+ - Optimize `Array#[]=` by moving the implementation to JavaScript and inlining type checks (#2383)
20
+ - Optimize internal runtime passing of block-options (#2383)
21
+ - Compile `case` statements as `switch` whenever possible (#2411)
22
+ - Improve performance with optimized common method/iter implementation shortcuts (#2401)
23
+
2
24
  ### Fixed
25
+
26
+ - Fix `Regexp.new`, previously `\A` and `\z` didn't match beginning and end of input (#2079)
27
+ - Fix exception during `Hash#each` and `Hash#each_key` if keys get deleted during the loop (#2403)
28
+ - Fix defining multiple methods with the same block (#2397)
29
+ - A few edge cases of conditional calls combined with setters, e.g. `foo&.bar = 123` (#2402)
30
+ - Correct String#to_proc and method_missing compatibility (#2418)
31
+ - Exit REPL respecting the exit status number (#2396)
32
+
33
+ ### Internal
34
+
35
+ - Rewriters refactor, fix interaction between cache and inverted runner (#2400)
36
+
37
+ <!--
38
+ ### Internal
3
39
  ### Changed
4
40
  ### Added
5
41
  ### Removed
6
42
  ### Deprecated
7
- ### Internal
8
43
  -->
@@ -1,33 +1,58 @@
1
1
  Benchmark.ips do |x|
2
2
  %x{
3
+ const c_foo = 'foo'
4
+ const v_foo = 'foo'
5
+ const cfoo = Symbol('foo')
6
+ var vfoo = Symbol('foo')
7
+ const cgfoo = Symbol.for('foo')
8
+ var vgfoo = Symbol.for('foo')
9
+
3
10
  var o = {}
4
- o[Symbol.for('foo')] = 123
5
- o.foo = 123
6
- var foo = Symbol('foo')
7
- var gfoo = Symbol.for('foo')
8
- o[foo] = 123
9
- var a = 0, b = 0, c = 0
11
+ o[cfoo] = 1
12
+ o[cgfoo] = 1
13
+ o[c_foo] = 1
14
+ o[vfoo] = 1
15
+
16
+ let a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0, a8 = 0
10
17
  }
11
18
 
19
+ x.report('const string ref') do
20
+ `a1 += o[c_foo]`
21
+ end
22
+
23
+ x.report('var string ref') do
24
+ `a2 += o[v_foo]`
25
+ end
26
+
12
27
  x.report('live global symbol') do
13
- `a += o[Symbol.for('foo')]`
28
+ `a3 += o[Symbol.for('foo')]`
14
29
  end
15
30
 
16
- x.report('stored global symbol') do
17
- `a += o[gfoo]`
31
+ x.report('const global symbol') do
32
+ `a4 += o[cgfoo]`
18
33
  end
19
34
 
20
- x.report('stored symbol') do
21
- `a += o[foo]`
35
+ x.report('var global symbol') do
36
+ `a5 += o[vgfoo]`
37
+ end
38
+
39
+ x.report('const symbol') do
40
+ `a6 += o[cfoo]`
41
+ end
42
+
43
+ x.report('var symbol') do
44
+ `a6 += o[vfoo]`
22
45
  end
23
46
 
24
47
  x.report('ident') do
25
- `b += o.foo`
48
+ `a7 += o.foo`
26
49
  end
27
50
 
28
- x.report('string') do
29
- `c += o['foo']`
51
+ x.report('live string') do
52
+ `a8 += o['foo']`
30
53
  end
31
54
 
55
+ x.time = 10
56
+
32
57
  x.compare!
33
58
  end
data/docs/releasing.md CHANGED
@@ -10,8 +10,8 @@ _This guide is a work-in-progress._
10
10
  ## Updating the changelog
11
11
 
12
12
  - Ensure all the unreleased changes are documented in UNRELEASED.md
13
- - Run `bin/rake changelog VERSION=v1.2.3` specifying the version number you're about to release
14
- - Empty UNRELEASED.md
13
+ - [skip for pre-releases] Run `bin/rake changelog VERSION=v1.2.3` specifying the version number you're about to release
14
+ - [skip for pre-releases] Empty UNRELEASED.md
15
15
 
16
16
  ## The commit
17
17
 
@@ -34,3 +34,11 @@ _This guide is a work-in-progress._
34
34
  ## Opal CDN
35
35
 
36
36
  - Run `bin/release v1.2.3`
37
+
38
+ ## Prepare for the next release
39
+
40
+ - Skip this step if releasing a stable release
41
+ - Create a new pull request that:
42
+ - Updates a version to `v1.x.0.dev` in both `lib/opal/version.rb` and `opal/corelib/constants.rb`
43
+ - Remember to merge that PR before merging anything else next once we decide to not release any more point releases from `master`.
44
+
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ast'
4
+ require 'parser/ast/node'
5
+
6
+ module Opal
7
+ module AST
8
+ class Matcher
9
+ def initialize(&block)
10
+ @root = instance_exec(&block)
11
+ end
12
+
13
+ def s(type, *children)
14
+ Node.new(type, children)
15
+ end
16
+
17
+ def cap(capture)
18
+ Node.new(:capture, [capture])
19
+ end
20
+
21
+ def match(ast)
22
+ @captures = []
23
+ @root.match(ast, self) || (return false)
24
+ @captures
25
+ end
26
+
27
+ def inspect
28
+ "#<Opal::AST::Matcher: #{@root.inspect}>"
29
+ end
30
+
31
+ attr_accessor :captures
32
+
33
+ Node = Struct.new(:type, :children) do
34
+ def match(ast, matcher)
35
+ return false if ast.nil?
36
+
37
+ ast_parts = [ast.type] + ast.children
38
+ self_parts = [type] + children
39
+
40
+ return false if ast_parts.length != self_parts.length
41
+
42
+ ast_parts.length.times.all? do |i|
43
+ ast_elem = ast_parts[i]
44
+ self_elem = self_parts[i]
45
+
46
+ if self_elem.is_a?(Node) && self_elem.type == :capture
47
+ capture = true
48
+ self_elem = self_elem.children.first
49
+ end
50
+
51
+ res = case self_elem
52
+ when Node
53
+ self_elem.match(ast_elem, matcher)
54
+ when Array
55
+ self_elem.include?(ast_elem)
56
+ when :*
57
+ true
58
+ else
59
+ self_elem == ast_elem
60
+ end
61
+
62
+ matcher.captures << ast_elem if capture
63
+ res
64
+ end
65
+ end
66
+
67
+ def inspect
68
+ if type == :capture
69
+ "{#{children.first.inspect}}"
70
+ else
71
+ "s(#{type.inspect}, #{children.inspect[1..-2]})"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/opal/cache.rb CHANGED
@@ -41,7 +41,7 @@ module Opal
41
41
 
42
42
  data || begin
43
43
  compiler = yield
44
- cache.set(key, compiler)
44
+ cache.set(key, compiler) unless compiler.dynamic_cache_result
45
45
  compiler
46
46
  end
47
47
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'opal/cli_runners/system_runner'
4
+
3
5
  module Opal
4
6
  module CliRunners
5
7
  class Applescript
data/lib/opal/compiler.rb CHANGED
@@ -217,9 +217,16 @@ module Opal
217
217
  # Comments from the source code
218
218
  attr_reader :comments
219
219
 
220
+ # Method calls made in this file
221
+ attr_reader :method_calls
222
+
220
223
  # Magic comment flags extracted from the leading comments
221
224
  attr_reader :magic_comments
222
225
 
226
+ # Set if some rewritter caused a dynamic cache result, meaning it's not
227
+ # fit to be cached
228
+ attr_accessor :dynamic_cache_result
229
+
223
230
  def initialize(source, options = {})
224
231
  @source = source
225
232
  @indent = ''
@@ -227,8 +234,10 @@ module Opal
227
234
  @options = options
228
235
  @comments = Hash.new([])
229
236
  @case_stmt = nil
237
+ @method_calls = Set.new
230
238
  @option_values = {}
231
239
  @magic_comments = {}
240
+ @dynamic_cache_result = false
232
241
  end
233
242
 
234
243
  # Compile some ruby code to a string.
@@ -260,9 +269,12 @@ module Opal
260
269
  :main
261
270
  end
262
271
 
263
- @sexp = s(:top, sexp || s(:nil)).tap { |i| i.meta[:kind] = kind }
264
- @comments = ::Parser::Source::Comment.associate_locations(sexp, comments)
265
- @magic_comments = MagicComments.parse(sexp, comments)
272
+ @sexp = sexp.tap { |i| i.meta[:kind] = kind }
273
+
274
+ first_node = sexp.children.first if sexp.children.first.location
275
+
276
+ @comments = ::Parser::Source::Comment.associate_locations(first_node, comments)
277
+ @magic_comments = MagicComments.parse(first_node, comments)
266
278
  @eof_content = EofContent.new(tokens, @source).eof
267
279
  end
268
280
 
@@ -287,9 +299,8 @@ module Opal
287
299
  )
288
300
  end
289
301
 
290
- # Method calls made in this file
291
- def method_calls
292
- @method_calls ||= Set.new
302
+ def record_method_call(mid)
303
+ @method_calls << mid
293
304
  end
294
305
 
295
306
  alias async_await_before_typecasting async_await
@@ -353,9 +364,7 @@ module Opal
353
364
  @indent
354
365
  end
355
366
 
356
- # Create a new sexp using the given parts. Even though this just
357
- # returns an array, it must be used incase the internal structure
358
- # of sexps does change.
367
+ # Create a new sexp using the given parts.
359
368
  def s(type, *children)
360
369
  ::Opal::AST::Node.new(type, children)
361
370
  end
@@ -8,7 +8,7 @@ require 'opal/rewriters/break_finder'
8
8
  module Opal
9
9
  module Nodes
10
10
  class CallNode < Base
11
- handle :send
11
+ handle :send, :csend
12
12
 
13
13
  attr_reader :recvr, :meth, :arglist, :iter
14
14
 
@@ -43,15 +43,19 @@ module Opal
43
43
  # handle some methods specially
44
44
  # some special methods need to skip compilation, so we pass the default as a block
45
45
  handle_special do
46
- compiler.method_calls << meth.to_sym if record_method?
47
-
48
- # if trying to access an lvar in eval or irb mode
49
- return compile_eval_var if using_eval?
50
-
51
- # if trying to access an lvar in irb mode
52
- return compile_irb_var if using_irb?
53
-
54
- default_compile
46
+ compiler.record_method_call meth
47
+
48
+ with_wrapper do
49
+ if using_eval?
50
+ # if trying to access an lvar in eval or irb mode
51
+ compile_eval_var
52
+ elsif using_irb?
53
+ # if trying to access an lvar in irb mode
54
+ compile_irb_var
55
+ else
56
+ default_compile
57
+ end
58
+ end
55
59
  end
56
60
  end
57
61
 
@@ -81,13 +85,18 @@ module Opal
81
85
  # to a method body. This is some kind of protection from method calls
82
86
  # like 'a(a {}) { 1 }'.
83
87
  def invoke_using_send?
84
- iter || splat?
88
+ iter || splat? || call_is_writer_that_needs_handling?
85
89
  end
86
90
 
87
91
  def invoke_using_refinement?
88
92
  !scope.scope.collect_refinements_temps.empty?
89
93
  end
90
94
 
95
+ # Is it a conditional send, ie. `foo&.bar`?
96
+ def csend?
97
+ @sexp.type == :csend
98
+ end
99
+
91
100
  def default_compile
92
101
  if auto_await?
93
102
  push 'await '
@@ -143,17 +152,19 @@ module Opal
143
152
  end
144
153
 
145
154
  def compile_receiver
146
- push recv(receiver_sexp)
155
+ push @conditional_recvr || recv(receiver_sexp)
147
156
  end
148
157
 
149
158
  def compile_method_name
150
159
  push ", '#{meth}'"
151
160
  end
152
161
 
153
- def compile_arguments
154
- push ', '
162
+ def compile_arguments(skip_comma = false)
163
+ push ', ' unless skip_comma
155
164
 
156
- if splat?
165
+ if @with_writer_temp
166
+ push @with_writer_temp
167
+ elsif splat?
157
168
  push expr(arglist)
158
169
  elsif arglist.children.empty?
159
170
  push '[]'
@@ -197,16 +208,13 @@ module Opal
197
208
  mid_to_jsid meth.to_s
198
209
  end
199
210
 
200
- def record_method?
201
- true
202
- end
203
-
204
211
  # Used to generate the code to use this sexp as an ivar var reference
205
212
  def compile_irb_var
206
213
  with_temp do |tmp|
207
214
  lvar = meth
208
215
  call = s(:send, s(:self), meth.intern, s(:arglist))
209
- push "((#{tmp} = Opal.irb_vars.#{lvar}) == null ? ", expr(call), " : #{tmp})"
216
+ ref = "(typeof #{lvar} !== 'undefined') ? #{lvar} : "
217
+ push "((#{tmp} = Opal.irb_vars.#{lvar}) == null ? ", ref, expr(call), " : #{tmp})"
210
218
  end
211
219
  end
212
220
 
@@ -255,7 +263,7 @@ module Opal
255
263
  if invoke_using_refinement?
256
264
  compile_default.call
257
265
  elsif compiler.inline_operators?
258
- compiler.method_calls << operator.to_sym if record_method?
266
+ compiler.record_method_call operator
259
267
  helper :"rb_#{name}"
260
268
  lhs, rhs = expr(recvr), expr(arglist)
261
269
 
@@ -414,13 +422,8 @@ module Opal
414
422
 
415
423
  scope.nesting
416
424
  push "Opal.Binding.$new("
417
- push " function($code, $value) {"
418
- push " if (typeof $value === 'undefined') {"
419
- push " return eval($code);"
420
- push " }"
421
- push " else {"
422
- push " return eval($code + ' = $value');"
423
- push " }"
425
+ push " function($code) {"
426
+ push " return eval($code);"
424
427
  push " },"
425
428
  push " ", scope.scope_locals.map(&:to_s).inspect, ","
426
429
  push " ", scope.self, ","
@@ -450,6 +453,48 @@ module Opal
450
453
  )
451
454
  end
452
455
 
456
+ def with_wrapper(&block)
457
+ if csend? && !@conditional_recvr
458
+ handle_conditional_send do
459
+ with_wrapper(&block)
460
+ end
461
+ elsif call_is_writer_that_needs_handling?
462
+ handle_writer(&block)
463
+ else
464
+ yield
465
+ end
466
+ end
467
+
468
+ def call_is_writer_that_needs_handling?
469
+ (expr? || recv?) && (meth.to_s =~ /^\w+=$/ || meth == :[]=)
470
+ end
471
+
472
+ # Handle safe-operator calls: foo&.bar / foo&.bar ||= baz / ...
473
+ def handle_conditional_send
474
+ # temporary variable that stores method receiver
475
+ receiver_temp = scope.new_temp
476
+ push "#{receiver_temp} = ", expr(recvr)
477
+
478
+ # execute the sexp only if the receiver isn't nil
479
+ push ", (#{receiver_temp} === nil || #{receiver_temp} == null) ? nil : "
480
+ @conditional_recvr = receiver_temp
481
+ yield
482
+ wrap '(', ')'
483
+ end
484
+
485
+ def handle_writer
486
+ with_temp do |temp|
487
+ push "(#{temp} = "
488
+ compile_arguments(true)
489
+ push ", "
490
+ @with_writer_temp = temp
491
+ yield
492
+ @with_writer_temp = false
493
+ push ", "
494
+ push "#{temp}[#{temp}.length - 1])"
495
+ end
496
+ end
497
+
453
498
  class DependencyResolver
454
499
  def initialize(compiler, sexp, missing_dynamic_require = nil)
455
500
  @compiler = compiler
@@ -10,6 +10,37 @@ module Opal
10
10
  children :mid, :inline_args, :stmts
11
11
 
12
12
  def compile
13
+ compile_body_or_shortcut
14
+
15
+ blockopts = []
16
+
17
+ blockopts << "$$arity: #{arity}"
18
+
19
+ if compiler.arity_check?
20
+ blockopts << "$$parameters: #{parameters_code}"
21
+ end
22
+
23
+ if compiler.parse_comments?
24
+ blockopts << "$$comments: #{comments_code}"
25
+ end
26
+
27
+ if compiler.enable_source_location?
28
+ blockopts << "$$source_location: #{source_location}"
29
+ end
30
+
31
+ if blockopts.length == 1
32
+ push ", #{arity}"
33
+ elsif blockopts.length > 1
34
+ push ', {', blockopts.join(', '), '}'
35
+ end
36
+
37
+ wrap_with_definition
38
+
39
+ scope.nesting if @define_nesting
40
+ scope.relative_access if @define_relative_access
41
+ end
42
+
43
+ def compile_body
13
44
  inline_params = nil
14
45
  scope_name = nil
15
46
 
@@ -51,33 +82,6 @@ module Opal
51
82
  unshift "async "
52
83
  end
53
84
  line '}'
54
-
55
- blockopts = []
56
-
57
- blockopts << "$$arity: #{arity}"
58
-
59
- if compiler.arity_check?
60
- blockopts << "$$parameters: #{parameters_code}"
61
- end
62
-
63
- if compiler.parse_comments?
64
- blockopts << "$$comments: #{comments_code}"
65
- end
66
-
67
- if compiler.enable_source_location?
68
- blockopts << "$$source_location: #{source_location}"
69
- end
70
-
71
- if blockopts.length == 1
72
- push ", #{arity}"
73
- elsif blockopts.length > 1
74
- push ', {', blockopts.join(', '), '}'
75
- end
76
-
77
- wrap_with_definition
78
-
79
- scope.nesting if @define_nesting
80
- scope.relative_access if @define_relative_access
81
85
  end
82
86
 
83
87
  def wrap_with_definition
@@ -31,6 +31,8 @@ module Opal
31
31
  push '$alias_gvar(', new_name_str, ', ', old_name_str, ')'
32
32
  when :dsym, :sym # This is a method alias: alias a b
33
33
  helper :alias
34
+ compiler.record_method_call old_name.children.last if old_name.type == :sym
35
+
34
36
  push "$alias(#{scope.self}, ", expr(new_name), ', ', expr(old_name), ')'
35
37
  else # Nothing else is available, but just in case, drop an error
36
38
  error "Opal doesn't know yet how to alias with #{new_name.type}"
@@ -72,22 +72,22 @@ module Opal
72
72
  case mid
73
73
  when :==
74
74
  helper :eqeq
75
- compiler.method_calls << mid
75
+ compiler.record_method_call mid
76
76
  [fragment('$eqeq('), expr(receiver), fragment(', '), expr(args.first), fragment(')')]
77
77
  when :===
78
78
  helper :eqeqeq
79
- compiler.method_calls << mid
79
+ compiler.record_method_call mid
80
80
  [fragment('$eqeqeq('), expr(receiver), fragment(', '), expr(args.first), fragment(')')]
81
81
  when :!=
82
82
  helper :neqeq
83
- compiler.method_calls << mid
83
+ compiler.record_method_call mid
84
84
  [fragment('$neqeq('), expr(receiver), fragment(', '), expr(args.first), fragment(')')]
85
85
  end
86
86
  elsif args.count == 0
87
87
  case mid
88
88
  when :!
89
89
  helper :not
90
- compiler.method_calls << mid
90
+ compiler.record_method_call mid
91
91
  [fragment('$not('), expr(receiver), fragment(')')]
92
92
  end
93
93
  end
@@ -114,25 +114,6 @@ module Opal
114
114
  end
115
115
  end
116
116
  end
117
-
118
- # Usefule for safe-operator calls: foo&.bar / foo&.bar ||= baz / ...
119
- #
120
- # @param recvr [sexp_pushable] The receiver of the call that will be
121
- # stored in a temporary variable
122
- # @yields receiver_temp [String] the name of the temporary variable
123
- # holding the ref to the original receiver, inside the block
124
- # an expr() should be pushed.
125
- #
126
- def conditional_send(recvr)
127
- # temporary variable that stores method receiver
128
- receiver_temp = scope.new_temp
129
- push "#{receiver_temp} = ", recvr
130
-
131
- # execute the sexp only if the receiver isn't nil
132
- push ", (#{receiver_temp} === nil || #{receiver_temp} == null) ? nil : "
133
- yield receiver_temp
134
- wrap '(', ')'
135
- end
136
117
  end
137
118
  end
138
119
  end