opal 0.9.0.beta2 → 0.9.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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.jshintrc +2 -1
  3. data/.travis.yml +8 -4
  4. data/CHANGELOG.md +270 -251
  5. data/CONTRIBUTING.md +41 -0
  6. data/README.md +8 -8
  7. data/Rakefile +1 -1
  8. data/lib/opal/cli.rb +1 -0
  9. data/lib/opal/cli_options.rb +2 -2
  10. data/lib/opal/cli_runners.rb +1 -0
  11. data/lib/opal/cli_runners/nashorn.rb +58 -0
  12. data/lib/opal/cli_runners/phantom.js +11 -9
  13. data/lib/opal/compiler.rb +2 -0
  14. data/lib/opal/erb.rb +2 -1
  15. data/lib/opal/nodes/def.rb +15 -6
  16. data/lib/opal/nodes/helpers.rb +1 -1
  17. data/lib/opal/nodes/literal.rb +1 -1
  18. data/lib/opal/nodes/masgn.rb +79 -29
  19. data/lib/opal/nodes/module.rb +1 -1
  20. data/lib/opal/nodes/top.rb +6 -2
  21. data/lib/opal/parser.rb +1 -0
  22. data/lib/opal/parser/grammar.rb +3160 -3083
  23. data/lib/opal/parser/grammar.y +39 -11
  24. data/lib/opal/parser/lexer.rb +15 -13
  25. data/lib/opal/parser/sexp.rb +5 -0
  26. data/lib/opal/version.rb +1 -1
  27. data/opal/corelib/array.rb +21 -6
  28. data/opal/corelib/basic_object.rb +17 -4
  29. data/opal/corelib/class.rb +1 -1
  30. data/opal/corelib/constants.rb +2 -2
  31. data/opal/corelib/enumerator.rb +2 -0
  32. data/opal/corelib/hash.rb +50 -50
  33. data/opal/corelib/helpers.rb +1 -1
  34. data/opal/corelib/io.rb +9 -6
  35. data/opal/corelib/kernel.rb +37 -9
  36. data/opal/corelib/module.rb +6 -17
  37. data/opal/corelib/nil.rb +4 -0
  38. data/opal/corelib/number.rb +0 -4
  39. data/opal/corelib/runtime.js +637 -662
  40. data/opal/corelib/struct.rb +7 -3
  41. data/spec/filters/bugs/array.rb +4 -9
  42. data/spec/filters/bugs/basicobject.rb +1 -15
  43. data/spec/filters/bugs/date.rb +4 -26
  44. data/spec/filters/bugs/enumerable.rb +26 -1
  45. data/spec/filters/bugs/enumerator.rb +4 -4
  46. data/spec/filters/bugs/exception.rb +1 -7
  47. data/spec/filters/bugs/float.rb +14 -0
  48. data/spec/filters/bugs/hash.rb +0 -2
  49. data/spec/filters/bugs/integer.rb +1 -1
  50. data/spec/filters/bugs/kernel.rb +47 -66
  51. data/spec/filters/bugs/language.rb +1 -2
  52. data/spec/filters/bugs/module.rb +11 -14
  53. data/spec/filters/bugs/numeric.rb +3 -3
  54. data/spec/filters/bugs/proc.rb +0 -1
  55. data/spec/filters/bugs/range.rb +2 -3
  56. data/spec/filters/bugs/regexp.rb +15 -17
  57. data/spec/filters/bugs/set.rb +2 -2
  58. data/spec/filters/bugs/string.rb +29 -5
  59. data/spec/filters/bugs/{strscan.rb → stringscanner.rb} +9 -10
  60. data/spec/filters/bugs/struct.rb +0 -4
  61. data/spec/filters/bugs/time.rb +2 -3
  62. data/spec/filters/bugs/unboundmethod.rb +3 -0
  63. data/spec/filters/unsupported/freeze.rb +2 -1
  64. data/spec/filters/unsupported/taint.rb +2 -0
  65. data/spec/lib/parser/aref_spec.rb +10 -0
  66. data/spec/lib/parser/lambda_spec.rb +14 -0
  67. data/spec/lib/parser/op_asgn_spec.rb +17 -0
  68. data/spec/lib/parser/return_spec.rb +5 -0
  69. data/spec/lib/parser/unary_spec.rb +4 -0
  70. data/spec/lib/sprockets/erb_spec.rb +1 -1
  71. data/spec/mspec/opal/formatters.rb +159 -0
  72. data/{lib → spec}/mspec/opal/runner.rb +0 -160
  73. data/spec/opal/core/hash/internals_spec.rb +162 -162
  74. data/spec/opal/core/kernel/at_exit_spec.rb +70 -0
  75. data/spec/opal/core/kernel/extend_spec.rb +1 -1
  76. data/spec/opal/core/kernel/instance_variables_spec.rb +56 -0
  77. data/spec/opal/core/language/equal_spec.rb +8 -0
  78. data/spec/opal/core/language/predefined_spec.rb +1 -1
  79. data/spec/opal/core/language/ternary_operator_spec.rb +14 -0
  80. data/spec/opal/core/language/versions/{hash_1.9.rb → hash_1_9_spec.rb} +5 -0
  81. data/spec/opal/core/module/fixtures/classes.rb +11 -1
  82. data/spec/opal/core/module/method_lookup_spec.rb +13 -0
  83. data/spec/opal/core/runtime/super_spec.rb +10 -0
  84. data/spec/opal/stdlib/native/new_spec.rb +84 -0
  85. data/spec/opal/stdlib/strscan/scan_spec.rb +11 -0
  86. data/spec/rubyspecs +6 -9
  87. data/spec/spec_helper.rb +9 -21
  88. data/stdlib/date.rb +121 -1
  89. data/stdlib/js.rb +13 -5
  90. data/stdlib/json.rb +3 -3
  91. data/stdlib/nashorn.rb +5 -0
  92. data/stdlib/nashorn/file.rb +13 -0
  93. data/stdlib/nashorn/io.rb +2 -0
  94. data/stdlib/native.rb +12 -4
  95. data/stdlib/nodejs/io.rb +5 -2
  96. data/stdlib/opal/platform.rb +19 -0
  97. data/stdlib/phantomjs.rb +4 -0
  98. data/stdlib/strscan.rb +1 -1
  99. data/tasks/building.rake +2 -1
  100. data/tasks/testing.rake +29 -34
  101. data/tasks/testing/{phantomjs1-sprockets.js → sprockets-phantomjs.js} +2 -20
  102. data/vendored-minitest/minitest/test.rb +7 -15
  103. metadata +32 -9
  104. data/lib/mspec/opal/main.rb.erb +0 -9
data/CONTRIBUTING.md CHANGED
@@ -23,6 +23,8 @@ the guides.
23
23
 
24
24
  If unsure about having satisfied any of the above points ask in the [Gitter channel](https://gitter.im/opal/opal) or just open the issue/pull-request asking for help. There's a good chance someone will help you through the necessary steps.
25
25
 
26
+ **A note on squashing commits in pull-requests:** There's no need to have a single commit in a PR, rather it's better to have focused commits with specific changes and just avoid the sequence of changes and reverts that tell an interesting story but make the use of `git blame` quite problematic. _That said these are quite loose requirements in the spirit of keeping contributing enjoyable 🤓_
27
+
26
28
  ## Quick Start
27
29
 
28
30
  Fork https://github.com/opal/opal, then clone the fork to your machine:
@@ -194,3 +196,42 @@ Array#permutation_truncates_Float_arguments
194
196
  Array#permutation_returns_an_Enumerator_which_works_as_expected_even_when_the_array_was_modified 0.056
195
197
  Array#permutation_generates_from_a_defensive_copy,_ignoring_mutations 0.038
196
198
  ```
199
+
200
+
201
+ ## Parser
202
+
203
+ ## Enabling debug output from Racc
204
+
205
+ To enable debug output in the Racc grammar set the `RACC_DEBUG` env var and recompile the grammar. While the env variable is set the parser will output the debug info for the parsing process.
206
+
207
+ ```bash
208
+ $ export RACC_DEBUG=true
209
+ $ bundle exec rake racc # recompile the grammar
210
+ $ bundle exec bin/opal --sexp -e "42" # ask Opal for the SExp of some code
211
+
212
+ read :tINTEGER(tINTEGER) [42, [1, 0]]
213
+
214
+ shift tINTEGER
215
+ [ (tINTEGER [42, [1, 0]]) ]
216
+
217
+ …cut…
218
+
219
+ shift $end
220
+ [ (program (:int, 42)) ($end [false, [1, 2]]) ($end [false, [1, 2]]) ]
221
+
222
+ goto 403
223
+ [ 0 1 97 403 ]
224
+
225
+ accept
226
+
227
+ (:int, 42)
228
+ ```
229
+
230
+ When done unset the env variable and recompile the grammar.
231
+
232
+ ```bash
233
+ $ unset RACC_DEBUG
234
+ $ bundle exec rake racc # recompile the grammar
235
+ $ bundle exec bin/opal --sexp -e "42" # ask Opal for the SExp of some code
236
+ (:int, 42)
237
+ ```
data/README.md CHANGED
@@ -11,7 +11,7 @@ Opal is [hosted on GitHub](http://github.com/opal/opal). Chat is available on *G
11
11
  Ask questions on [stackoverflow (tag #opalrb)](http://stackoverflow.com/questions/ask?tags=opalrb). Get the [Opalist newsletter](http://opalist.co) for updates and community news.
12
12
 
13
13
  [![Inline docs](http://inch-ci.org/github/opal/opal.svg?branch=master&style=flat)](http://opalrb.org/docs/api)
14
- [![Gitter chat](http://img.shields.io/badge/gitter-opal%2Fopal-009966.svg?style=flat)](https://gitter.im/opal/opal)
14
+ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/opal/opal?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
15
15
  [![Stack Overflow](http://img.shields.io/badge/stackoverflow-%23opalrb-orange.svg?style=flat)](http://stackoverflow.com/questions/ask?tags=opalrb)
16
16
 
17
17
  ## Usage
@@ -29,7 +29,7 @@ puts 'Hello world!'
29
29
  Then from the terminal
30
30
 
31
31
  ```bash
32
- $ opal --compile app.rb > app.js # The Opal runtime is included by default but
32
+ $ opal --compile app.rb > app.js # The Opal runtime is included by default
33
33
  # but can be skipped with the --no-opal flag
34
34
  ```
35
35
 
@@ -168,20 +168,20 @@ What code is supposed to run where?
168
168
  * `opal/` is the runtime+corelib for our implementation (runs in browser).
169
169
  * `stdlib/` is our implementation of Ruby's stdlib. It is optional (runs in browser).
170
170
 
171
- ### lib
171
+ ### lib/
172
172
 
173
- The `lib` directory holds the Opal parser/compiler used to compile Ruby
173
+ The `lib` directory holds the **Opal parser/compiler** used to compile Ruby
174
174
  into JavaScript. It is also built ready for the browser into `opal-parser.js`
175
175
  to allow compilation in any JavaScript environment.
176
176
 
177
- ### corelib
177
+ ### opal/
178
178
 
179
- This directory holds the Opal runtime and corelib implemented in Ruby and
179
+ This directory holds the **Opal runtime and corelib** implemented in Ruby and
180
180
  JavaScript.
181
181
 
182
- ### stdlib
182
+ ### stdlib/
183
183
 
184
- Holds the stdlib that Opal currently supports. This includes `Observable`,
184
+ Holds the **stdlib currently supported by Opal**. This includes `Observable`,
185
185
  `StringScanner`, `Date`, etc.
186
186
 
187
187
  ## Browser support
data/Rakefile CHANGED
@@ -11,4 +11,4 @@ import 'tasks/building.rake'
11
11
  import 'tasks/linting.rake'
12
12
  import 'tasks/benchmarking.rake'
13
13
 
14
- task :default => [:rspec, :mspec_node, :cruby_tests]
14
+ task :default => [:rspec, :mspec_nodejs, :cruby_tests]
data/lib/opal/cli.rb CHANGED
@@ -76,6 +76,7 @@ module Opal
76
76
  when :nodejs; CliRunners::Nodejs.new(output)
77
77
  when :phantomjs; CliRunners::Phantomjs.new(output)
78
78
  when :applescript; CliRunners::AppleScript.new(output)
79
+ when :nashorn; CliRunners::Nashorn.new(output)
79
80
  else raise ArgumentError, @runner_type.inspect
80
81
  end
81
82
  end
@@ -84,7 +84,7 @@ module Opal
84
84
  options[:compile] = true
85
85
  end
86
86
 
87
- on('-R', '--runner RUNNER', %w[nodejs server phantomjs applescript], 'Choose the runner: nodejs (default), server') do |runner|
87
+ on('-R', '--runner RUNNER', %w[nodejs server phantomjs applescript nashorn], 'Choose the runner: nodejs (default), server') do |runner|
88
88
  options[:runner] = runner.to_sym
89
89
  end
90
90
 
@@ -93,7 +93,7 @@ module Opal
93
93
  options[:port] = port.to_i
94
94
  end
95
95
 
96
- on('--no-exit', 'Do not append a Kernel#exit at the end of file') do |no_exit|
96
+ on('-E', '--no-exit', 'Do not append a Kernel#exit at the end of file') do |no_exit|
97
97
  options[:no_exit] = true
98
98
  end
99
99
 
@@ -9,3 +9,4 @@ require 'opal/cli_runners/apple_script'
9
9
  require 'opal/cli_runners/phantomjs'
10
10
  require 'opal/cli_runners/nodejs'
11
11
  require 'opal/cli_runners/server'
12
+ require 'opal/cli_runners/nashorn'
@@ -0,0 +1,58 @@
1
+ require 'opal/cli_runners'
2
+ require 'opal/paths'
3
+
4
+ module Opal
5
+ module CliRunners
6
+ class Nashorn
7
+ def initialize(output)
8
+ @output ||= output
9
+ end
10
+ attr_reader :output, :exit_status
11
+
12
+ def puts(*args)
13
+ output.puts(*args)
14
+ end
15
+
16
+ def run(code, argv)
17
+ require 'tempfile'
18
+ tempfile = Tempfile.new('opal-nashorn-runner-')
19
+ tempfile.write code
20
+ tempfile.close
21
+ system_with_output({}, 'jjs', tempfile.path , *argv)
22
+ rescue Errno::ENOENT
23
+ raise MissingNashorn, 'Please install JDK to be able to run Opal scripts.'
24
+ end
25
+
26
+ # Let's support fake IO objects like StringIO
27
+ def system_with_output(env, *cmd)
28
+ if IO.try_convert(output)
29
+ system(env,*cmd)
30
+ @exit_status = $?.exitstatus
31
+ return
32
+ end
33
+
34
+ if RUBY_PLATFORM == 'java'
35
+ # JRuby has issues in dealing with subprocesses (at least up to 1.7.15)
36
+ # @headius told me it's mostly fixed on master, but while we wait for it
37
+ # to ship here's a tempfile workaround.
38
+ require 'tempfile'
39
+ require 'shellwords'
40
+ tempfile = Tempfile.new('opal-nashorn-output')
41
+ system(env,cmd.shelljoin+" > #{tempfile.path}")
42
+ @exit_status = $?.exitstatus
43
+ captured_output = File.read tempfile.path
44
+ tempfile.close
45
+ else
46
+ require 'open3'
47
+ captured_output, status = Open3.capture2(env,*cmd)
48
+ @exit_status = status.exitstatus
49
+ end
50
+
51
+ output.write captured_output
52
+ end
53
+
54
+ class MissingNashorn < RunnerError
55
+ end
56
+ end
57
+ end
58
+ end
@@ -33,12 +33,14 @@ page.onCallback = function(data) {
33
33
  }
34
34
  };
35
35
 
36
- page.content = '<!doctype html>'+
37
- '<html>'+
38
- ' <head><meta charset="utf-8"/></head>'+
39
- " <body><script>//<![CDATA[\n"+
40
- opal_code+
41
- " //]]></script>"+
42
- ' <script>callPhantom(["exit", 0]);</script></body>\n'+
43
- ' </body>'+
44
- '</html>';
36
+
37
+ page.onInitialized = function() {
38
+ page.evaluate('function(code) {window.eval(code)}', opal_code)
39
+ };
40
+
41
+ page.setContent("<!doctype html>\n"+
42
+ "<html>"+
43
+ "<head><meta charset='utf-8'/></head><body>"+
44
+ "</body></html>",
45
+ 'http://www.example.com');
46
+
data/lib/opal/compiler.rb CHANGED
@@ -116,6 +116,8 @@ module Opal
116
116
  # are operators compiled inline
117
117
  compiler_option :inline_operators, true, :as => :inline_operators?
118
118
 
119
+ compiler_option :eval, false, as: :eval?
120
+
119
121
  # @return [String] The compiled ruby code
120
122
  attr_reader :result
121
123
 
data/lib/opal/erb.rb CHANGED
@@ -71,7 +71,8 @@ module Opal
71
71
 
72
72
  def find_code(result)
73
73
  result.gsub(/<%([\s\S]+?)%>/) do
74
- "\")\n#{ $1 }\noutput_buffer.append(\""
74
+ inner = $1.gsub(/\\"/, '"')
75
+ "\")\n#{ inner }\noutput_buffer.append(\""
75
76
  end
76
77
  end
77
78
 
@@ -79,14 +79,22 @@ module Opal
79
79
  scope.locals.delete(rest_arg[1])
80
80
  end
81
81
 
82
+ if scope.uses_zuper
83
+ add_local '$zuper'
84
+ add_local '$zuper_index'
85
+
86
+ line "$zuper = [];"
87
+ line "for($zuper_index = 0; $zuper_index < arguments.length; $zuper_index++) {"
88
+ line " $zuper[$zuper_index] = arguments[$zuper_index];"
89
+ line "}"
90
+ end
91
+
82
92
  unshift "\n#{current_indent}", scope.to_vars
83
93
 
84
94
  line arity_code if arity_code
85
95
 
86
96
  line stmt_code
87
97
 
88
- unshift "var $zuper = $slice.call(arguments, 0);" if scope.uses_zuper
89
-
90
98
  if scope.catch_return
91
99
  unshift "try {\n"
92
100
  line "} catch ($returner) { if ($returner === Opal.returner) { return $returner.$v }"
@@ -139,11 +147,12 @@ module Opal
139
147
  def compile_rest_arg
140
148
  if rest_arg and rest_arg[1]
141
149
  splat = variable(rest_arg[1].to_sym)
150
+ add_local '$splat_index'
142
151
  line "var array_size = arguments.length - #{argc};"
143
152
  line "if(array_size < 0) array_size = 0;"
144
153
  line "var #{splat} = new Array(array_size);"
145
- line "for(var arg_index = 0; arg_index < array_size; arg_index++) {"
146
- line " #{splat}[arg_index] = arguments[arg_index + #{argc}];"
154
+ line "for($splat_index = 0; $splat_index < array_size; $splat_index++) {"
155
+ line " #{splat}[$splat_index] = arguments[$splat_index + #{argc}];"
147
156
  line "}"
148
157
  end
149
158
  end
@@ -199,14 +208,14 @@ module Opal
199
208
  arg_name = kwarg[1]
200
209
  var_name = variable(arg_name.to_s)
201
210
  add_local var_name
202
- line "if ((#{var_name} = $kwargs.smap['#{arg_name}']) == null) {"
211
+ line "if ((#{var_name} = $kwargs.$$smap['#{arg_name}']) == null) {"
203
212
  line " #{var_name} = ", expr(kwarg[2])
204
213
  line "}"
205
214
  when :kwarg
206
215
  arg_name = kwarg[1]
207
216
  var_name = variable(arg_name.to_s)
208
217
  add_local var_name
209
- line "if ((#{var_name} = $kwargs.smap['#{arg_name}']) == null) {"
218
+ line "if ((#{var_name} = $kwargs.$$smap['#{arg_name}']) == null) {"
210
219
  line " throw new Error('expecting keyword arg: #{arg_name}')"
211
220
  line "}"
212
221
  when :kwrestarg
@@ -12,7 +12,7 @@ module Opal
12
12
  ES3_RESERVED_WORD_EXCLUSIVE = /#{REGEXP_START}(?:int|byte|char|goto|long|final|float|short|double|native|throws|boolean|abstract|volatile|transient|synchronized)#{REGEXP_END}/
13
13
 
14
14
  # Prototype special properties.
15
- PROTO_SPECIAL_PROPS = /#{REGEXP_START}(?:constructor|__proto__|__parent__|__noSuchMethod__|__count__)#{REGEXP_END}/
15
+ PROTO_SPECIAL_PROPS = /#{REGEXP_START}(?:constructor|displayName|__proto__|__parent__|__noSuchMethod__|__count__)#{REGEXP_END}/
16
16
 
17
17
  # Prototype special methods.
18
18
  PROTO_SPECIAL_METHODS = /#{REGEXP_START}(?:hasOwnProperty|valueOf)#{REGEXP_END}/
@@ -129,7 +129,7 @@ module Opal
129
129
  push part.inspect
130
130
  elsif part.type == :evstr
131
131
  push "("
132
- push expr(part[1])
132
+ push part[1] ? expr(part[1]) : '""'
133
133
  push ")"
134
134
  elsif part.type == :str
135
135
  push part[1].inspect
@@ -3,58 +3,108 @@ require 'opal/nodes/base'
3
3
  module Opal
4
4
  module Nodes
5
5
  class MassAssignNode < Base
6
- handle :masgn
6
+ # TODO: does this work for cvars?? constants??
7
+ SIMPLE_ASSIGNMENT = [:lasgn, :iasgn, :lvar, :gasgn]
7
8
 
9
+ handle :masgn
8
10
  children :lhs, :rhs
9
11
 
10
12
  def compile
11
- tmp = scope.new_temp
12
- len = 0 # how many rhs items are we sure we have
13
+ array = scope.new_temp
13
14
 
14
15
  if rhs.type == :array
15
- len = rhs.size - 1
16
- push "#{tmp} = ", expr(rhs)
16
+ push "#{array} = ", expr(rhs)
17
+ compile_masgn(lhs.children, array, rhs.size - 1)
18
+ push ", #{array}" # a mass assignment evaluates to the RHS
17
19
  elsif rhs.type == :to_ary
18
- push "#{tmp} = Opal.to_ary(", expr(rhs[1]), ")"
20
+ retval = scope.new_temp
21
+ push "#{retval} = ", expr(rhs[1])
22
+ push ", #{array} = Opal.to_ary(#{retval})"
23
+ compile_masgn(lhs.children, array)
24
+ push ", #{retval}"
25
+ scope.queue_temp(retval)
19
26
  elsif rhs.type == :splat
20
- push "#{tmp} = Opal.to_a(", expr(rhs[1]), ")"
27
+ push "#{array} = Opal.to_a(", expr(rhs[1]), ")"
28
+ compile_masgn(lhs.children, array)
29
+ push ", #{array}"
21
30
  else
22
31
  raise "unsupported mlhs type"
23
32
  end
24
33
 
25
- lhs.children.each_with_index do |child, idx|
26
- push ', '
34
+ scope.queue_temp(array)
35
+ end
36
+
37
+ # 'len' is how many rhs items are we sure we have
38
+ def compile_masgn(lhs_items, array, len = nil)
39
+ pre_splat = lhs_items.take_while { |child| child.type != :splat }
40
+ post_splat = lhs_items.drop(pre_splat.size)
41
+
42
+ pre_splat.each_with_index do |child, idx|
43
+ compile_assignment(child, array, idx, len)
44
+ end
45
+
46
+ unless post_splat.empty?
47
+ splat = post_splat.shift
27
48
 
28
- if child.type == :splat
29
- if part = child[1]
30
- part = part.dup
31
- part << s(:js_tmp, "$slice.call(#{tmp}, #{idx})")
49
+ if post_splat.empty? # trailing splat
50
+ if part = splat[1]
51
+ part = part.dup << s(:js_tmp, "$slice.call(#{array}, #{pre_splat.size})")
52
+ push ', '
32
53
  push expr(part)
33
54
  end
34
55
  else
35
- if idx >= len
36
- assign = s(:js_tmp, "(#{tmp}[#{idx}] == null ? nil : #{tmp}[#{idx}])")
37
- else
38
- assign = s(:js_tmp, "#{tmp}[#{idx}]")
56
+ tmp = scope.new_temp # end index for items consumed by splat
57
+ push ", #{tmp} = #{array}.length - #{post_splat.size}"
58
+ push ", #{tmp} = (#{tmp} < #{pre_splat.size}) ? #{pre_splat.size} : #{tmp}"
59
+
60
+ if part = splat[1]
61
+ part = part.dup << s(:js_tmp, "$slice.call(#{array}, #{pre_splat.size}, #{tmp})")
62
+ push ', '
63
+ push expr(part)
39
64
  end
40
65
 
41
- part = child.dup
42
- if child.type == :lasgn or child.type == :iasgn or child.type == :lvar or child.type == :gasgn
43
- part << assign
44
- elsif child.type == :call
45
- part[2] = "#{part[2]}=".to_sym
46
- part.last << assign
47
- elsif child.type == :attrasgn
48
- part.last << assign
49
- else
50
- raise "Bad lhs for masgn"
66
+ post_splat.each_with_index do |child, idx|
67
+ if idx == 0
68
+ compile_assignment(child, array, tmp)
69
+ else
70
+ compile_assignment(child, array, "#{tmp} + #{idx}")
71
+ end
51
72
  end
52
73
 
53
- push expr(part)
74
+ scope.queue_temp(tmp)
54
75
  end
55
76
  end
77
+ end
78
+
79
+ def compile_assignment(child, array, idx, len = nil)
80
+ if !len || idx >= len
81
+ assign = s(:js_tmp, "(#{array}[#{idx}] == null ? nil : #{array}[#{idx}])")
82
+ else
83
+ assign = s(:js_tmp, "#{array}[#{idx}]")
84
+ end
85
+
86
+ part = child.dup
87
+ if SIMPLE_ASSIGNMENT.include?(child.type)
88
+ part << assign
89
+ elsif child.type == :call
90
+ part[2] = "#{part[2]}=".to_sym
91
+ part.last << assign
92
+ elsif child.type == :attrasgn
93
+ part.last << assign
94
+ elsif child.type == :array
95
+ # nested destructuring
96
+ tmp = scope.new_temp
97
+ push ", (#{tmp} = Opal.to_ary(#{assign[1]})"
98
+ compile_masgn(child.children, tmp)
99
+ push ')'
100
+ scope.queue_temp(tmp)
101
+ return
102
+ else
103
+ raise "Bad child node in masgn LHS: #{child}. LHS: #{lhs}"
104
+ end
56
105
 
57
- scope.queue_temp tmp
106
+ push ', '
107
+ push expr(part)
58
108
  end
59
109
  end
60
110
  end