opal 1.7.4 → 1.8.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (260) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -0
  3. data/.github/workflows/build.yml +9 -9
  4. data/.rubocop/todo.yml +2 -2
  5. data/.rubocop.yml +17 -10
  6. data/.rubocop_todo.yml +311 -0
  7. data/CHANGELOG.md +1 -15
  8. data/README.md +7 -7
  9. data/UNRELEASED.md +92 -1
  10. data/benchmark-ips/bm_block_vs_yield.rb +3 -0
  11. data/benchmark-ips/bm_slice_or_not.rb +53 -0
  12. data/docs/bridging.md +112 -0
  13. data/docs/compiled_ruby.md +10 -10
  14. data/docs/getting_started.md +18 -22
  15. data/docs/releasing.md +8 -16
  16. data/lib/opal/cli_runners/chrome_cdp_interface.rb +1 -0
  17. data/lib/opal/cli_runners/firefox_cdp_interface.rb +1 -0
  18. data/lib/opal/compiler.rb +33 -1
  19. data/lib/opal/nodes/args/extract_kwoptarg.rb +2 -1
  20. data/lib/opal/nodes/call.rb +1 -1
  21. data/lib/opal/nodes/call_special.rb +71 -47
  22. data/lib/opal/nodes/closure.rb +15 -7
  23. data/lib/opal/nodes/defined.rb +1 -1
  24. data/lib/opal/nodes/hash.rb +14 -30
  25. data/lib/opal/nodes/if.rb +37 -29
  26. data/lib/opal/nodes/literal.rb +15 -7
  27. data/lib/opal/nodes/rescue.rb +1 -1
  28. data/lib/opal/nodes/x_string.rb +13 -0
  29. data/lib/opal/parser/patch.rb +1 -0
  30. data/lib/opal/rewriters/for_rewriter.rb +36 -24
  31. data/lib/opal/source_map/file.rb +1 -1
  32. data/lib/opal/version.rb +1 -1
  33. data/opal/corelib/array/pack.rb +1 -0
  34. data/opal/corelib/array.rb +110 -64
  35. data/opal/corelib/basic_object.rb +1 -0
  36. data/opal/corelib/binding.rb +2 -0
  37. data/opal/corelib/boolean.rb +1 -0
  38. data/opal/corelib/class.rb +28 -8
  39. data/opal/corelib/comparable.rb +1 -0
  40. data/opal/corelib/complex.rb +3 -1
  41. data/opal/corelib/constants.rb +2 -2
  42. data/opal/corelib/dir.rb +2 -0
  43. data/opal/corelib/enumerable.rb +3 -2
  44. data/opal/corelib/enumerator/arithmetic_sequence.rb +3 -1
  45. data/opal/corelib/enumerator/chain.rb +1 -0
  46. data/opal/corelib/enumerator/generator.rb +1 -0
  47. data/opal/corelib/enumerator/lazy.rb +1 -0
  48. data/opal/corelib/enumerator/yielder.rb +2 -0
  49. data/opal/corelib/enumerator.rb +1 -0
  50. data/opal/corelib/error/errno.rb +2 -0
  51. data/opal/corelib/error.rb +22 -0
  52. data/opal/corelib/file.rb +1 -0
  53. data/opal/corelib/hash.rb +224 -519
  54. data/opal/corelib/helpers.rb +1 -0
  55. data/opal/corelib/io.rb +2 -0
  56. data/opal/corelib/irb.rb +2 -0
  57. data/opal/corelib/kernel/format.rb +1 -0
  58. data/opal/corelib/kernel.rb +70 -15
  59. data/opal/corelib/main.rb +2 -0
  60. data/opal/corelib/marshal/read_buffer.rb +2 -0
  61. data/opal/corelib/marshal/write_buffer.rb +2 -0
  62. data/opal/corelib/math/polyfills.rb +2 -0
  63. data/opal/corelib/math.rb +1 -0
  64. data/opal/corelib/method.rb +2 -0
  65. data/opal/corelib/module.rb +45 -5
  66. data/opal/corelib/nil.rb +3 -1
  67. data/opal/corelib/number.rb +30 -1
  68. data/opal/corelib/numeric.rb +2 -0
  69. data/opal/corelib/object_space.rb +1 -0
  70. data/opal/corelib/pack_unpack/format_string_parser.rb +2 -0
  71. data/opal/corelib/proc.rb +30 -28
  72. data/opal/corelib/process.rb +2 -0
  73. data/opal/corelib/random/formatter.rb +2 -0
  74. data/opal/corelib/random/math_random.js.rb +2 -0
  75. data/opal/corelib/random/mersenne_twister.rb +2 -0
  76. data/opal/corelib/random/seedrandom.js.rb +2 -0
  77. data/opal/corelib/random.rb +1 -0
  78. data/opal/corelib/range.rb +35 -13
  79. data/opal/corelib/rational.rb +3 -1
  80. data/opal/corelib/regexp.rb +1 -0
  81. data/opal/corelib/runtime.js +297 -259
  82. data/opal/corelib/set.rb +2 -0
  83. data/opal/corelib/string/encoding.rb +3 -0
  84. data/opal/corelib/string/unpack.rb +2 -0
  85. data/opal/corelib/string.rb +56 -20
  86. data/opal/corelib/struct.rb +4 -2
  87. data/opal/corelib/time.rb +2 -1
  88. data/opal/corelib/trace_point.rb +2 -0
  89. data/opal/corelib/unsupported.rb +2 -0
  90. data/opal/corelib/variables.rb +2 -0
  91. data/opal.gemspec +2 -2
  92. data/spec/filters/bugs/array.rb +58 -2
  93. data/spec/filters/bugs/basicobject.rb +7 -0
  94. data/spec/filters/bugs/bigdecimal.rb +1 -2
  95. data/spec/filters/bugs/binding.rb +1 -0
  96. data/spec/filters/bugs/class.rb +2 -3
  97. data/spec/filters/bugs/complex.rb +3 -0
  98. data/spec/filters/bugs/date.rb +5 -2
  99. data/spec/filters/bugs/datetime.rb +1 -0
  100. data/spec/filters/bugs/delegate.rb +1 -2
  101. data/spec/filters/bugs/encoding.rb +1 -1
  102. data/spec/filters/bugs/enumerable.rb +11 -3
  103. data/spec/filters/bugs/enumerator.rb +15 -2
  104. data/spec/filters/bugs/exception.rb +9 -4
  105. data/spec/filters/bugs/file.rb +2 -0
  106. data/spec/filters/bugs/float.rb +1 -0
  107. data/spec/filters/bugs/freeze.rb +5 -49
  108. data/spec/filters/bugs/hash.rb +1 -13
  109. data/spec/filters/bugs/integer.rb +5 -6
  110. data/spec/filters/bugs/kernel.rb +12 -43
  111. data/spec/filters/bugs/language.rb +33 -15
  112. data/spec/filters/bugs/marshal.rb +63 -4
  113. data/spec/filters/bugs/method.rb +2 -10
  114. data/spec/filters/bugs/module.rb +18 -7
  115. data/spec/filters/bugs/objectspace.rb +2 -0
  116. data/spec/filters/bugs/pathname.rb +1 -0
  117. data/spec/filters/bugs/proc.rb +4 -2
  118. data/spec/filters/bugs/random.rb +0 -3
  119. data/spec/filters/bugs/range.rb +1 -2
  120. data/spec/filters/bugs/rational.rb +2 -0
  121. data/spec/filters/bugs/refinement.rb +19 -0
  122. data/spec/filters/bugs/regexp.rb +27 -5
  123. data/spec/filters/bugs/ruby-32.rb +0 -6
  124. data/spec/filters/bugs/set.rb +10 -2
  125. data/spec/filters/bugs/singleton.rb +0 -2
  126. data/spec/filters/bugs/string.rb +140 -2
  127. data/spec/filters/bugs/struct.rb +16 -10
  128. data/spec/filters/bugs/time.rb +56 -2
  129. data/spec/filters/bugs/trace_point.rb +1 -0
  130. data/spec/filters/bugs/unboundmethod.rb +4 -9
  131. data/spec/filters/bugs/warnings.rb +0 -1
  132. data/spec/filters/platform/firefox/exception.rb +3 -3
  133. data/spec/filters/platform/firefox/kernel.rb +1 -0
  134. data/spec/filters/platform/safari/exception.rb +2 -2
  135. data/spec/filters/platform/safari/float.rb +1 -0
  136. data/spec/filters/platform/safari/kernel.rb +1 -0
  137. data/spec/filters/platform/safari/literal_regexp.rb +2 -2
  138. data/spec/filters/unsupported/hash.rb +1 -0
  139. data/spec/lib/compiler_spec.rb +28 -17
  140. data/spec/mspec-opal/formatters.rb +2 -0
  141. data/spec/mspec-opal/runner.rb +2 -0
  142. data/spec/opal/core/array/dup_spec.rb +2 -0
  143. data/spec/opal/core/class/clone_spec.rb +36 -0
  144. data/spec/opal/core/exception_spec.rb +2 -0
  145. data/spec/opal/core/hash/internals_spec.rb +154 -206
  146. data/spec/opal/core/hash_spec.rb +2 -0
  147. data/spec/opal/core/iterable_props_spec.rb +2 -0
  148. data/spec/opal/core/kernel/at_exit_spec.rb +2 -0
  149. data/spec/opal/core/kernel/respond_to_spec.rb +2 -0
  150. data/spec/opal/core/language/arguments/mlhs_arg_spec.rb +2 -0
  151. data/spec/opal/core/language/safe_navigator_spec.rb +2 -0
  152. data/spec/opal/core/language/xstring_send_spec.rb +15 -0
  153. data/spec/opal/core/language/xstring_spec.rb +2 -0
  154. data/spec/opal/core/language_spec.rb +2 -0
  155. data/spec/opal/core/module_spec.rb +44 -0
  156. data/spec/opal/core/number/to_i_spec.rb +2 -0
  157. data/spec/opal/core/object_id_spec.rb +2 -6
  158. data/spec/opal/core/regexp/match_spec.rb +2 -0
  159. data/spec/opal/core/runtime/bridged_classes_spec.rb +38 -0
  160. data/spec/opal/core/runtime/constants_spec.rb +2 -0
  161. data/spec/opal/core/runtime/eval_spec.rb +2 -0
  162. data/spec/opal/core/runtime/exit_spec.rb +2 -0
  163. data/spec/opal/core/runtime/is_a_spec.rb +2 -0
  164. data/spec/opal/core/runtime/loaded_spec.rb +2 -0
  165. data/spec/opal/core/runtime/method_missing_spec.rb +2 -0
  166. data/spec/opal/core/runtime/rescue_spec.rb +2 -0
  167. data/spec/opal/core/runtime/string_spec.rb +2 -0
  168. data/spec/opal/core/runtime/truthy_spec.rb +2 -0
  169. data/spec/opal/core/runtime_spec.rb +2 -6
  170. data/spec/opal/core/string/to_sym_spec.rb +2 -0
  171. data/spec/opal/language/predefined_spec.rb +20 -0
  172. data/spec/opal/language/yield_spec.rb +43 -0
  173. data/spec/opal/stdlib/js_spec.rb +2 -0
  174. data/spec/opal/stdlib/native/alias_native_spec.rb +2 -0
  175. data/spec/opal/stdlib/native/array_spec.rb +2 -0
  176. data/spec/opal/stdlib/native/date_spec.rb +2 -0
  177. data/spec/opal/stdlib/native/each_spec.rb +2 -0
  178. data/spec/opal/stdlib/native/element_reference_spec.rb +2 -0
  179. data/spec/opal/stdlib/native/exposure_spec.rb +2 -0
  180. data/spec/opal/stdlib/native/ext_spec.rb +2 -0
  181. data/spec/opal/stdlib/native/hash_spec.rb +30 -2
  182. data/spec/opal/stdlib/native/initialize_spec.rb +2 -0
  183. data/spec/opal/stdlib/native/method_missing_spec.rb +2 -0
  184. data/spec/opal/stdlib/native/native_alias_spec.rb +2 -0
  185. data/spec/opal/stdlib/native/native_class_spec.rb +2 -0
  186. data/spec/opal/stdlib/native/native_module_spec.rb +2 -0
  187. data/spec/opal/stdlib/native/native_reader_spec.rb +2 -0
  188. data/spec/opal/stdlib/native/native_writer_spec.rb +2 -0
  189. data/spec/opal/stdlib/native/new_spec.rb +2 -0
  190. data/spec/opal/stdlib/native/struct_spec.rb +2 -0
  191. data/spec/ruby_specs +0 -2
  192. data/spec/spec_helper.rb +2 -0
  193. data/stdlib/await.rb +1 -0
  194. data/stdlib/base64.rb +2 -0
  195. data/stdlib/bigdecimal/bignumber.js.rb +2 -0
  196. data/stdlib/bigdecimal/util.rb +1 -0
  197. data/stdlib/bigdecimal.rb +4 -0
  198. data/stdlib/buffer/array.rb +2 -0
  199. data/stdlib/buffer/view.rb +2 -0
  200. data/stdlib/buffer.rb +2 -0
  201. data/stdlib/cgi.rb +14 -0
  202. data/stdlib/console.rb +2 -0
  203. data/stdlib/date/date_time.rb +2 -0
  204. data/stdlib/date.rb +2 -0
  205. data/stdlib/delegate.rb +5 -4
  206. data/stdlib/deno/base.rb +2 -0
  207. data/stdlib/deno/file.rb +2 -0
  208. data/stdlib/erb.rb +2 -0
  209. data/stdlib/gjs/io.rb +2 -0
  210. data/stdlib/gjs/kernel.rb +2 -0
  211. data/stdlib/headless_browser/base.rb +2 -0
  212. data/stdlib/headless_browser/file.rb +1 -0
  213. data/stdlib/headless_browser.rb +1 -0
  214. data/stdlib/js.rb +2 -0
  215. data/stdlib/json.rb +9 -15
  216. data/stdlib/logger.rb +2 -0
  217. data/stdlib/nashorn/file.rb +2 -0
  218. data/stdlib/nashorn/io.rb +2 -0
  219. data/stdlib/native.rb +48 -48
  220. data/stdlib/nodejs/base.rb +2 -0
  221. data/stdlib/nodejs/dir.rb +2 -0
  222. data/stdlib/nodejs/env.rb +2 -0
  223. data/stdlib/nodejs/file.rb +2 -0
  224. data/stdlib/nodejs/fileutils.rb +2 -0
  225. data/stdlib/nodejs/io.rb +2 -0
  226. data/stdlib/nodejs/js-yaml-3-6-1.js +1 -1
  227. data/stdlib/nodejs/kernel.rb +2 -0
  228. data/stdlib/nodejs/open-uri.rb +2 -0
  229. data/stdlib/nodejs/pathname.rb +2 -0
  230. data/stdlib/nodejs/require.rb +2 -0
  231. data/stdlib/nodejs/yaml.rb +9 -3
  232. data/stdlib/opal/miniracer.rb +2 -0
  233. data/stdlib/opal-parser.rb +8 -1
  234. data/stdlib/opal-platform.rb +2 -0
  235. data/stdlib/opal-replutils.rb +2 -0
  236. data/stdlib/open-uri.rb +4 -1
  237. data/stdlib/ostruct.rb +4 -2
  238. data/stdlib/pathname.rb +3 -1
  239. data/stdlib/pp.rb +1 -0
  240. data/stdlib/promise/v2.rb +24 -7
  241. data/stdlib/quickjs/io.rb +2 -0
  242. data/stdlib/quickjs/kernel.rb +2 -0
  243. data/stdlib/quickjs.rb +2 -0
  244. data/stdlib/securerandom.rb +2 -0
  245. data/stdlib/stringio.rb +2 -0
  246. data/stdlib/strscan.rb +2 -0
  247. data/stdlib/time.rb +2 -0
  248. data/stdlib/uri.rb +1 -0
  249. data/tasks/performance/optimization_status.rb +2 -0
  250. data/tasks/testing.rake +16 -11
  251. data/test/nodejs/test_await.rb +1 -0
  252. data/test/nodejs/test_dir.rb +2 -0
  253. data/test/nodejs/test_error.rb +2 -0
  254. data/test/nodejs/test_file.rb +2 -0
  255. data/test/nodejs/test_string.rb +2 -0
  256. data/test/nodejs/test_yaml.rb +20 -0
  257. data/test/opal/promisev2/test_always.rb +14 -0
  258. data/test/opal/unsupported_and_bugs.rb +0 -8
  259. metadata +26 -13
  260. data/spec/filters/bugs/openstruct.rb +0 -8
@@ -27,14 +27,12 @@ module Opal
27
27
  end
28
28
 
29
29
  def simple_keys?
30
- keys.all? { |key| %i[sym str].include?(key.type) }
30
+ keys.all? { |key| %i[sym str int].include?(key.type) }
31
31
  end
32
32
 
33
33
  def compile
34
34
  if has_kwsplat
35
35
  compile_merge
36
- elsif simple_keys?
37
- compile_hash2
38
36
  else
39
37
  compile_hash
40
38
  end
@@ -47,8 +45,6 @@ module Opal
47
45
  # Each kwsplat overrides previosly defined keys
48
46
  # Hash k/v pairs override previously defined kwsplat values
49
47
  def compile_merge
50
- helper :hash
51
-
52
48
  result, seq = [], []
53
49
 
54
50
  children.each do |child|
@@ -76,38 +72,26 @@ module Opal
76
72
  end
77
73
 
78
74
  # Compiles a hash without kwsplats
79
- # with complex keys.
75
+ # with simple or complex keys.
80
76
  def compile_hash
81
- helper :hash
82
-
83
77
  children.each_with_index do |pair, idx|
84
78
  key, value = pair.children
85
79
  push ', ' unless idx == 0
86
- push expr(key), ', ', expr(value)
87
- end
88
-
89
- wrap '$hash(', ')'
90
- end
91
-
92
- # Compiles a hash without kwsplats
93
- # and containing **only** string/symbols as keys.
94
- def compile_hash2
95
- hash_obj, hash_keys = {}, []
96
- helper :hash2
97
-
98
- keys.size.times do |idx|
99
- key = keys[idx].children[0].to_s.inspect
100
- hash_keys << key unless hash_obj.include? key
101
- hash_obj[key] = expr(values[idx])
80
+ if %i[sym str].include?(key.type)
81
+ push "[#{key.children[0].to_s.inspect}", ', ', expr(value), ']'
82
+ else
83
+ push '[', expr(key), ', ', expr(value), ']'
84
+ end
102
85
  end
103
86
 
104
- hash_keys.each_with_index do |key, idx|
105
- push ', ' unless idx == 0
106
- push "#{key}: "
107
- push hash_obj[key]
87
+ if keys.empty?
88
+ push '(new Map())'
89
+ elsif simple_keys?
90
+ wrap '(new Map([', ']))'
91
+ else
92
+ helper :hash_rehash
93
+ wrap '$hash_rehash(new Map([', ']))'
108
94
  end
109
-
110
- wrap "$hash2([#{hash_keys.join ', '}], {", '})'
111
95
  end
112
96
  end
113
97
 
data/lib/opal/nodes/if.rb CHANGED
@@ -392,45 +392,53 @@ module Opal
392
392
  end
393
393
  end
394
394
 
395
- class IFlipFlop < Base
396
- handle :iflipflop
397
-
398
- children :from, :to
399
-
400
- # Is this an exclusive flip flop? If no, run both branches
401
- def excl
402
- ""
403
- end
395
+ class BaseFlipFlop < Base
396
+ children :start_condition, :end_condition
404
397
 
405
- # We create a function that we put in the top scope, that stores the state of our
406
- # flip-flop. We pass to it functions that are ran with the current binding.
407
398
  def compile
408
399
  helper :truthy
409
400
 
410
- fun_name = top_scope.new_temp
411
- ff = "#{fun_name}.$$ff"
412
-
413
- push "(typeof #{fun_name} === 'undefined' ? (#{fun_name} = function(from, to){"
414
- push " if (typeof #{ff} === 'undefined') #{ff} = false;"
415
- push " var retval = #{ff};"
416
- push " if (!#{ff}) {"
417
- push " #{ff} = retval = $truthy(from());"
418
- push " }"
419
- push " #{excl}if (#{ff}) {"
420
- push " if ($truthy(to())) #{ff} = false;"
421
- push " }"
422
- push " return retval;"
423
- push "}) : #{fun_name})("
424
- push " function() { ", stmt(compiler.returns(from)), " },"
425
- push " function() { ", stmt(compiler.returns(to)), " }"
401
+ func_name = top_scope.new_temp
402
+ flip_flop_state = "#{func_name}.$$ff"
403
+
404
+ # Start function definition, checking and initializing it if necessary
405
+ push "(#{func_name} = #{func_name} || function(_start_func, _end_func){"
406
+
407
+ # If flip flop state is not defined, set it to false
408
+ push " var flip_flop = #{flip_flop_state} || false;"
409
+
410
+ # If flip flop state is false, call the 'start_condition' function and store its truthy result into flip flop state
411
+ push " if (!flip_flop) #{flip_flop_state} = flip_flop = $truthy(_start_func());"
412
+
413
+ # If flip flop state is true, call the 'end_condition' function and set flip flop state to false if 'end_condition' is truthy
414
+ push " #{excl}if (flip_flop && $truthy(_end_func())) #{flip_flop_state} = false;"
415
+
416
+ # Return current state of flip flop
417
+ push " return flip_flop;"
418
+
419
+ # End function definition
420
+ push "})("
421
+
422
+ # Call the function with 'start_condition' and 'end_condition' arguments wrapped in functions to ensure correct binding and delay evaluation
423
+ push " function() { ", stmt(compiler.returns(start_condition)), " },"
424
+ push " function() { ", stmt(compiler.returns(end_condition)), " }"
426
425
  push ")"
427
426
  end
428
427
  end
429
428
 
430
- class EFlipFlop < IFlipFlop
429
+ class IFlipFlop < BaseFlipFlop
430
+ handle :iflipflop
431
+
432
+ # Inclusive flip flop, check 'end_condition' in the same iteration when 'start_condition' is truthy
433
+ def excl
434
+ ""
435
+ end
436
+ end
437
+
438
+ class EFlipFlop < BaseFlipFlop
431
439
  handle :eflipflop
432
440
 
433
- # Is this an exclusive flip flop? If yes, run only a single branch
441
+ # Exclusive flip flop, check 'end_condition' in the next iteration after 'start_condition' is truthy
434
442
  def excl
435
443
  "else "
436
444
  end
@@ -5,14 +5,10 @@ require 'opal/nodes/base'
5
5
  module Opal
6
6
  module Nodes
7
7
  class ValueNode < Base
8
- handle :true, :false, :self, :nil
8
+ handle :true, :false, :nil
9
9
 
10
10
  def compile
11
- if type == :self
12
- push scope.self
13
- else
14
- push type.to_s
15
- end
11
+ push type.to_s
16
12
  end
17
13
 
18
14
  def self.truthy_optimize?
@@ -20,6 +16,14 @@ module Opal
20
16
  end
21
17
  end
22
18
 
19
+ class SelfNode < Base
20
+ handle :self
21
+
22
+ def compile
23
+ push scope.self
24
+ end
25
+ end
26
+
23
27
  class NumericNode < Base
24
28
  handle :int, :float
25
29
 
@@ -158,11 +162,15 @@ module Opal
158
162
  case value
159
163
  when ''
160
164
  push('/(?:)/')
161
- when /\(\?<=|\(\?<!/
165
+ when /\(\?[(<>#]|[*+?]\+/
162
166
  # Safari/WebKit will not execute javascript code if it contains a lookbehind literal RegExp
163
167
  # and they fail with "Syntax Error". This tricks their parser by disguising the literal RegExp
164
168
  # as string for the dynamic $regexp helper. Safari/Webkit will still fail to execute the RegExp,
165
169
  # but at least they will parse and run everything else.
170
+ #
171
+ # Also, let's compile a couple of more patterns into $regexp calls, as there are many syntax
172
+ # errors in RubySpec when ran in reverse, while there shouldn't be (they should be catchable
173
+ # errors) - at least since Node 17.
166
174
  static_as_dynamic(value)
167
175
  else
168
176
  push "#{Regexp.new(value).inspect}#{flags.join}"
@@ -211,7 +211,7 @@ module Opal
211
211
  line stmt(rescue_body)
212
212
  end
213
213
  end
214
- line '} finally { Opal.pop_exception(); }'
214
+ line '} finally { Opal.pop_exception($err); }'
215
215
  end
216
216
  line '}'
217
217
  end
@@ -6,6 +6,19 @@ module Opal
6
6
  handle :xstr
7
7
 
8
8
  def compile
9
+ if compiler.backtick_javascript_or_warn?
10
+ compile_javascript
11
+ else
12
+ compile_send
13
+ end
14
+ end
15
+
16
+ def compile_send
17
+ sexp = s(:send, nil, :`, s(:dstr, *children))
18
+ push process(sexp, @level)
19
+ end
20
+
21
+ def compile_javascript
9
22
  @should_add_semicolon = false
10
23
  unpacked_children = unpack_return(children)
11
24
  stripped_children = XStringNode.strip_empty_children(unpacked_children)
@@ -1,3 +1,4 @@
1
+ # backtick_javascript: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  if RUBY_ENGINE == 'opal'
@@ -34,40 +34,52 @@ module Opal
34
34
  # j = nil
35
35
  # [[1, 2], [3, 4]].each { |__jstmp| i, j = __jstmp }
36
36
  #
37
- def on_for(node)
38
- loop_variable, iterating_value, loop_body = *node
39
37
 
40
- iterating_lvars = LocalVariableAssigns.find(loop_variable) # [:i]
41
- lvars_declared_in_body = LocalVariableAssigns.find(loop_body) # [:j]
38
+ def on_for(node)
39
+ loop_variable, loop_range, loop_body = *node
42
40
 
43
- # i = nil; j = nil
44
- outer_assigns = (iterating_lvars + lvars_declared_in_body).map do |lvar_name|
45
- s(:lvdeclare, lvar_name)
46
- end
41
+ # Declare local variables used in the loop and the loop body at the outer scope
42
+ outer_assignments = generate_outer_assignments(loop_variable, loop_body)
47
43
 
48
- # :__jstmp
44
+ # Generate temporary loop variable
49
45
  tmp_loop_variable = self.class.next_tmp
50
46
  get_tmp_loop_variable = s(:js_tmp, tmp_loop_variable)
51
47
 
52
- loop_variable_assignment = case loop_variable.type
53
- when :mlhs # multiple left-hand statement like in "for i,j in [[1, 2], [3, 4]]"
54
- # i, j = __jstmp
55
- loop_variable.updated(:masgn, [loop_variable, get_tmp_loop_variable])
56
- else # single argument like "for i in (0..3)"
57
- # i = __jstmp
58
- loop_variable << get_tmp_loop_variable
59
- end
48
+ # Assign the loop variables in the loop body
49
+ loop_body = prepend_to_body(loop_body, assign_loop_variable(loop_variable, get_tmp_loop_variable))
50
+
51
+ # Transform the for-loop into each-loop with updated loop body
52
+ node = transform_for_to_each_loop(node, loop_range, tmp_loop_variable, loop_body)
53
+
54
+ node.updated(:begin, [*outer_assignments, node])
55
+ end
60
56
 
61
- loop_body = prepend_to_body(loop_body, loop_variable_assignment)
57
+ private
62
58
 
63
- node = node.updated(:send, [iterating_value, :each, # (0..3).each {
64
- node.updated(:iter, [s(:args, s(:arg, tmp_loop_variable)), # |__jstmp|
65
- process(loop_body)] # i = __jstmp; j = i + 1 }
66
- )]
67
- )
59
+ def generate_outer_assignments(loop_variable, loop_body)
60
+ loop_local_vars = LocalVariableAssigns.find(loop_variable)
61
+ body_local_vars = LocalVariableAssigns.find(loop_body)
62
+
63
+ (loop_local_vars + body_local_vars).map { |lvar_name| s(:lvdeclare, lvar_name) }
64
+ end
65
+
66
+ def assign_loop_variable(loop_variable, tmp_loop_variable)
67
+ case loop_variable.type
68
+ when :mlhs # multiple left-hand statement like in "for i,j in [[1, 2], [3, 4]]"
69
+ loop_variable.updated(:masgn, [loop_variable, tmp_loop_variable])
70
+ else # single argument like "for i in (0..3)"
71
+ loop_variable << tmp_loop_variable
72
+ end
73
+ end
68
74
 
69
- node.updated(:begin, [*outer_assigns, node])
75
+ # rubocop:disable Layout/MultilineMethodCallBraceLayout,Layout/MultilineArrayBraceLayout
76
+ def transform_for_to_each_loop(node, loop_range, tmp_loop_variable, loop_body)
77
+ node.updated(:send, [loop_range, :each, # (0..3).each {
78
+ node.updated(:iter, [s(:args, s(:arg, tmp_loop_variable)), # |__jstmp|
79
+ process(loop_body) # i = __jstmp; j = i + 1 }
80
+ ])])
70
81
  end
82
+ # rubocop:enable Layout/MultilineMethodCallBraceLayout,Layout/MultilineArrayBraceLayout
71
83
 
72
84
  class LocalVariableAssigns < Base
73
85
  attr_reader :result
@@ -134,7 +134,7 @@ class Opal::SourceMap::File
134
134
  absolute_mapping.map do |absolute_segment|
135
135
  segment = []
136
136
 
137
- segment[0] = absolute_segment[0] - reference_segment[0]
137
+ segment[0] = absolute_segment[0] - reference_segment[0]
138
138
  segment[1] = absolute_segment[1] - (reference_segment[1] || 0)
139
139
  segment[2] = absolute_segment[2] - (reference_segment[2] || 0)
140
140
  segment[3] = absolute_segment[3] - (reference_segment[3] || 0)
data/lib/opal/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  module Opal
4
4
  # WHEN RELEASING:
5
5
  # Remember to update RUBY_ENGINE_VERSION in opal/corelib/constants.rb too!
6
- VERSION = '1.7.4'
6
+ VERSION = '1.8.0.beta1'
7
7
  end
@@ -1,4 +1,5 @@
1
1
  # helpers: coerce_to
2
+ # backtick_javascript: true
2
3
 
3
4
  require 'corelib/pack_unpack/format_string_parser'
4
5
 
@@ -1,4 +1,5 @@
1
- # helpers: truthy, falsy, hash_ids, yield1, hash_get, hash_put, hash_delete, coerce_to, respond_to, deny_frozen_access, freeze
1
+ # helpers: truthy, falsy, yield1, hash_get, hash_put, hash_delete, coerce_to, respond_to, deny_frozen_access, freeze, opal32_init, opal32_add
2
+ # backtick_javascript: true
2
3
 
3
4
  require 'corelib/enumerable'
4
5
  require 'corelib/numeric'
@@ -68,6 +69,29 @@ class ::Array < `Array`
68
69
 
69
70
  if (raised) throw raised;
70
71
  }
72
+
73
+ function convertToArray(array) {
74
+ if (!array.$$is_array) {
75
+ array = $coerce_to(array, #{::Array}, 'to_ary');
76
+ }
77
+ return #{`array`.to_a};
78
+ }
79
+
80
+ function fast_push(arr, objects) {
81
+ // push.apply() for arrays longer than 32767 may cause various argument errors in browsers
82
+ // but it is significantly faster than a for loop, which pushes each element separately
83
+ // but apply() has a overhead by itself, for a small number of elements
84
+ // the for loop is significantly faster
85
+ // this is using the best option depending on objects.length
86
+ var length = objects.length;
87
+ if (length > 6 && length < 32767) {
88
+ arr.push.apply(arr, objects);
89
+ } else {
90
+ for (var i = 0; i < length; i++) {
91
+ arr.push(objects[i]);
92
+ }
93
+ }
94
+ }
71
95
  }
72
96
 
73
97
  def self.[](*objects)
@@ -91,7 +115,7 @@ class ::Array < `Array`
91
115
  }
92
116
 
93
117
  if (arguments.length === 0) {
94
- self.splice(0, self.length);
118
+ if (self.length > 0) self.splice(0, self.length);
95
119
  return self;
96
120
  }
97
121
 
@@ -135,13 +159,13 @@ class ::Array < `Array`
135
159
  end
136
160
 
137
161
  def &(other)
138
- other = if ::Array === other
139
- other.to_a
140
- else
141
- `$coerce_to(other, #{::Array}, 'to_ary')`.to_a
142
- end
143
-
144
162
  %x{
163
+ other = convertToArray(other)
164
+
165
+ if (self.length === 0 || other.length === 0) {
166
+ return [];
167
+ }
168
+
145
169
  var result = [], hash = #{{}}, i, length, item;
146
170
 
147
171
  for (i = 0, length = other.length; i < length; i++) {
@@ -160,11 +184,7 @@ class ::Array < `Array`
160
184
  end
161
185
 
162
186
  def |(other)
163
- other = if ::Array === other
164
- other.to_a
165
- else
166
- `$coerce_to(other, #{::Array}, 'to_ary')`.to_a
167
- end
187
+ other = `convertToArray(other)`
168
188
 
169
189
  %x{
170
190
  var hash = #{{}}, i, length, item;
@@ -203,21 +223,13 @@ class ::Array < `Array`
203
223
  end
204
224
 
205
225
  def +(other)
206
- other = if ::Array === other
207
- other.to_a
208
- else
209
- `$coerce_to(other, #{::Array}, 'to_ary')`.to_a
210
- end
226
+ other = `convertToArray(other)`
211
227
 
212
228
  `self.concat(other)`
213
229
  end
214
230
 
215
231
  def -(other)
216
- other = if ::Array === other
217
- other.to_a
218
- else
219
- `$coerce_to(other, #{::Array}, 'to_ary')`.to_a
220
- end
232
+ other = `convertToArray(other)`
221
233
 
222
234
  return [] if `self.length === 0`
223
235
  return `self.slice()` if `other.length === 0`
@@ -257,7 +269,7 @@ class ::Array < `Array`
257
269
  end
258
270
 
259
271
  %x{
260
- if (#{hash} === #{other.hash}) {
272
+ if (#{self} === #{other}) {
261
273
  return 0;
262
274
  }
263
275
 
@@ -696,11 +708,12 @@ class ::Array < `Array`
696
708
  return enum_for(:collect) { size } unless block_given?
697
709
 
698
710
  %x{
699
- var result = [];
711
+ var length = self.length;
712
+ var result = new Array(length);
700
713
 
701
- for (var i = 0, length = self.length; i < length; i++) {
714
+ for (var i = 0; i < length; i++) {
702
715
  var value = $yield1(block, self[i]);
703
- result.push(value);
716
+ result[i] = value;
704
717
  }
705
718
 
706
719
  return result;
@@ -849,11 +862,7 @@ class ::Array < `Array`
849
862
  `$deny_frozen_access(self)`
850
863
 
851
864
  others = others.map do |other|
852
- other = if ::Array === other
853
- other.to_a
854
- else
855
- `$coerce_to(other, #{::Array}, 'to_ary')`.to_a
856
- end
865
+ `other = convertToArray(other)`
857
866
 
858
867
  if other.equal?(self)
859
868
  other = other.dup
@@ -1223,7 +1232,7 @@ class ::Array < `Array`
1223
1232
  result.push(ary);
1224
1233
  break;
1225
1234
  default:
1226
- result.push.apply(result, _flatten(ary, level - 1));
1235
+ fast_push(result, _flatten(ary, level - 1));
1227
1236
  }
1228
1237
  }
1229
1238
  return result;
@@ -1267,27 +1276,31 @@ class ::Array < `Array`
1267
1276
  `$freeze(self)`
1268
1277
  end
1269
1278
 
1279
+ `var $hash_ids`
1280
+
1270
1281
  def hash
1271
1282
  %x{
1272
1283
  var top = ($hash_ids === undefined),
1273
- result = ['A'],
1284
+ result = $opal32_init(),
1274
1285
  hash_id = self.$object_id(),
1275
1286
  item, i, key;
1276
1287
 
1277
- try {
1278
- if (top) {
1279
- $hash_ids = Object.create(null);
1280
- }
1288
+ result = $opal32_add(result, 0xA);
1289
+ result = $opal32_add(result, self.length);
1281
1290
 
1282
- // return early for recursive structures
1283
- if ($hash_ids[hash_id]) {
1284
- return 'self';
1285
- }
1291
+ if (top) {
1292
+ $hash_ids = Object.create(null);
1293
+ }
1294
+ // return early for recursive structures
1295
+ else if ($hash_ids[hash_id]) {
1296
+ return $opal32_add(result, 0x01010101);
1297
+ }
1286
1298
 
1299
+ try {
1287
1300
  for (key in $hash_ids) {
1288
1301
  item = $hash_ids[key];
1289
1302
  if (#{eql?(`item`)}) {
1290
- return 'self';
1303
+ return $opal32_add(result, 0x01010101);
1291
1304
  }
1292
1305
  }
1293
1306
 
@@ -1295,10 +1308,10 @@ class ::Array < `Array`
1295
1308
 
1296
1309
  for (i = 0; i < self.length; i++) {
1297
1310
  item = self[i];
1298
- result.push(item.$hash());
1311
+ result = $opal32_add(result, item.$hash());
1299
1312
  }
1300
1313
 
1301
- return result.join(',');
1314
+ return result;
1302
1315
  } finally {
1303
1316
  if (top) {
1304
1317
  $hash_ids = undefined;
@@ -1410,11 +1423,54 @@ class ::Array < `Array`
1410
1423
  end
1411
1424
 
1412
1425
  def intersection(*arrays)
1413
- arrays.reduce(to_a.dup) { |a, b| a & b }
1426
+ %x{
1427
+ if (arrays.length === 0) {
1428
+ return #{to_a.dup};
1429
+ }
1430
+ arrays = arrays.map(convertToArray);
1431
+ if (self.length === 0) {
1432
+ return [];
1433
+ }
1434
+ }
1435
+
1436
+ arrays = arrays.sort_by(&:length)
1437
+ # When self is the smallest among the arrays
1438
+ if `self.length < arrays[0].length`
1439
+ return arrays.reduce(self, &:&)
1440
+ end
1441
+
1442
+ # First, calculate intersection of argument arrays.
1443
+ # Array#& is faster when the argument size is small.
1444
+ # So `largest & shortest & second_shortest & ...` would be the fastest.
1445
+ largest = `arrays.pop()`
1446
+ intersection_of_args = arrays.reduce(largest, &:&)
1447
+
1448
+ # self array must come last to maintain the order
1449
+ self & intersection_of_args
1414
1450
  end
1415
1451
 
1416
1452
  def intersect?(other)
1417
- !intersection(other).empty?
1453
+ %x{
1454
+ var small, large, hash = #{{}}, i, length;
1455
+ if (self.length < other.length) {
1456
+ small = self;
1457
+ large = other;
1458
+ } else {
1459
+ small = other;
1460
+ large = self;
1461
+ }
1462
+
1463
+ for (i = 0, length = small.length; i < length; i++) {
1464
+ $hash_put(hash, small[i], true);
1465
+ }
1466
+
1467
+ for (i = 0, length = large.length; i < length; i++) {
1468
+ if ($hash_get(hash, large[i])) {
1469
+ return true;
1470
+ }
1471
+ }
1472
+ return false;
1473
+ }
1418
1474
  end
1419
1475
 
1420
1476
  def join(sep = nil)
@@ -1705,9 +1761,7 @@ class ::Array < `Array`
1705
1761
  %x{
1706
1762
  $deny_frozen_access(self);
1707
1763
 
1708
- for (var i = 0, length = objects.length; i < length; i++) {
1709
- self.push(objects[i]);
1710
- }
1764
+ fast_push(self, objects);
1711
1765
  }
1712
1766
 
1713
1767
  self
@@ -1762,15 +1816,11 @@ class ::Array < `Array`
1762
1816
  def replace(other)
1763
1817
  `$deny_frozen_access(self)`
1764
1818
 
1765
- other = if ::Array === other
1766
- other.to_a
1767
- else
1768
- `$coerce_to(other, #{::Array}, 'to_ary')`.to_a
1769
- end
1819
+ other = `convertToArray(other)`
1770
1820
 
1771
1821
  %x{
1772
- self.splice(0, self.length);
1773
- self.push.apply(self, other);
1822
+ if (self.length > 0) self.splice(0, self.length);
1823
+ fast_push(self, other);
1774
1824
  }
1775
1825
 
1776
1826
  self
@@ -2284,10 +2334,10 @@ class ::Array < `Array`
2284
2334
  for (i = 0; i < len; i++) {
2285
2335
  ary = #{::Opal.coerce_to?(`array[i]`, ::Array, :to_ary)};
2286
2336
  if (!ary.$$is_array) {
2287
- #{::Kernel.raise ::TypeError, "wrong element type #{`ary`.class} at #{`i`} (expected array)"}
2337
+ #{::Kernel.raise ::TypeError, "wrong element type #{`array[i]`.class} at #{`i`} (expected array)"}
2288
2338
  }
2289
2339
  if (ary.length !== 2) {
2290
- #{::Kernel.raise ::ArgumentError, "wrong array length at #{`i`} (expected 2, was #{`ary`.length})"}
2340
+ #{::Kernel.raise ::ArgumentError, "element has wrong array length at #{`i`} (expected 2, was #{`ary`.length})"}
2291
2341
  }
2292
2342
  key = ary[0];
2293
2343
  val = ary[1];
@@ -2305,11 +2355,7 @@ class ::Array < `Array`
2305
2355
  max = nil
2306
2356
 
2307
2357
  each do |row|
2308
- row = if ::Array === row
2309
- row.to_a
2310
- else
2311
- `$coerce_to(row, #{::Array}, 'to_ary')`.to_a
2312
- end
2358
+ `row = convertToArray(row)`
2313
2359
 
2314
2360
  max ||= `row.length`
2315
2361
 
@@ -1,4 +1,5 @@
1
1
  # use_strict: true
2
+ # backtick_javascript: true
2
3
 
3
4
  class ::BasicObject
4
5
  def initialize(*)
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  class ::Binding
2
4
  # @private
3
5
  def initialize(jseval, scope_variables = [], receiver = undefined, source_location = nil)