opal 0.9.0.beta2 → 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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