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
@@ -51,7 +51,6 @@ module Opal
51
51
  end
52
52
 
53
53
  alias on_iter process_regular_node
54
- alias on_top process_regular_node
55
54
  alias on_zsuper process_regular_node
56
55
  alias on_jscall on_send
57
56
  alias on_jsattr process_regular_node
@@ -124,6 +123,18 @@ module Opal
124
123
  error.location = current_node.loc if current_node
125
124
  raise error
126
125
  end
126
+
127
+ def on_top(node)
128
+ node = process_regular_node(node)
129
+ node.meta[:dynamic_cache_result] = true if @dynamic_cache_result
130
+ node
131
+ end
132
+
133
+ # Called when a given transformation is deemed to be dynamic, so
134
+ # that cache is conditionally disabled for a given file.
135
+ def dynamic!
136
+ @dynamic_cache_result = true
137
+ end
127
138
  end
128
139
  end
129
140
  end
@@ -36,6 +36,7 @@ module Opal
36
36
  _recvr, method_name, *args = *node
37
37
 
38
38
  if rubyspec_dsl?(method_name)
39
+ dynamic!
39
40
  spec_name, _ = *args.first
40
41
  begin
41
42
  @specs_stack.push(spec_name)
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.4.1'
6
+ VERSION = '1.5.0.rc1'
7
7
  end
@@ -441,20 +441,18 @@ class ::Array < `Array`
441
441
  end
442
442
 
443
443
  def []=(index, value, extra = undefined)
444
+ data = nil
444
445
  %x{
445
446
  var i, size = self.length;
446
- }
447
447
 
448
- if ::Range === index
449
- data = if ::Array === value
450
- value.to_a
451
- elsif value.respond_to? :to_ary
452
- value.to_ary.to_a
453
- else
454
- [value]
455
- end
448
+ if (index.$$is_range) {
449
+ if (value.$$is_array)
450
+ data = #{value.to_a};
451
+ else if (#{value.respond_to? :to_ary})
452
+ data = #{value.to_ary.to_a};
453
+ else
454
+ data = [value];
456
455
 
457
- %x{
458
456
  var exclude = index.excl,
459
457
  from = index.begin === nil ? 0 : $coerce_to(index.begin, Opal.Integer, 'to_int'),
460
458
  to = index.end === nil ? -1 : $coerce_to(index.end, Opal.Integer, 'to_int');
@@ -489,24 +487,21 @@ class ::Array < `Array`
489
487
  }
490
488
 
491
489
  return value;
492
- }
493
- else
494
- if `extra === undefined`
495
- length = 1
496
- else
497
- length = value
498
- value = extra
499
-
500
- data = if ::Array === value
501
- value.to_a
502
- elsif value.respond_to? :to_ary
503
- value.to_ary.to_a
504
- else
505
- [value]
506
- end
507
- end
490
+ } else {
491
+ if (extra === undefined) {
492
+ #{length = 1}
493
+ } else {
494
+ length = value;
495
+ value = extra;
496
+
497
+ if (value.$$is_array)
498
+ data = #{value.to_a};
499
+ else if (#{value.respond_to? :to_ary})
500
+ data = #{value.to_ary.to_a};
501
+ else
502
+ data = [value];
503
+ }
508
504
 
509
- %x{
510
505
  var old;
511
506
 
512
507
  index = $coerce_to(index, #{::Integer}, 'to_int');
@@ -540,7 +535,7 @@ class ::Array < `Array`
540
535
 
541
536
  return value;
542
537
  }
543
- end
538
+ }
544
539
  end
545
540
 
546
541
  def any?(pattern = undefined, &block)
@@ -1,8 +1,9 @@
1
1
  class ::Binding
2
2
  # @private
3
- def initialize(jseval, scope_variables, receiver, source_location)
3
+ def initialize(jseval, scope_variables = [], receiver = undefined, source_location = nil)
4
4
  @jseval, @scope_variables, @receiver, @source_location = \
5
5
  jseval, scope_variables, receiver, source_location
6
+ receiver = js_eval('self') unless `typeof receiver !== undefined`
6
7
  end
7
8
 
8
9
  def js_eval(*args)
@@ -20,7 +21,10 @@ class ::Binding
20
21
  end
21
22
 
22
23
  def local_variable_set(symbol, value)
23
- js_eval(symbol, value)
24
+ `Opal.Binding.tmp_value = value`
25
+ js_eval("#{symbol} = Opal.Binding.tmp_value")
26
+ `delete Opal.Binding.tmp_value`
27
+ value
24
28
  end
25
29
 
26
30
  def local_variables
@@ -46,5 +50,11 @@ module ::Kernel
46
50
  end
47
51
  end
48
52
 
49
- TOPLEVEL_BINDING = binding
50
- `#{TOPLEVEL_BINDING}.source_location = ["<main>", 0]`
53
+ TOPLEVEL_BINDING = ::Binding.new(
54
+ %x{
55
+ function(js) {
56
+ return (new Function("self", "return " + js))(self);
57
+ }
58
+ },
59
+ [], self, ['<main>', 0]
60
+ )
@@ -1,9 +1,9 @@
1
1
  ::RUBY_PLATFORM = 'opal'
2
2
  ::RUBY_ENGINE = 'opal'
3
3
  ::RUBY_VERSION = '3.1.0'
4
- ::RUBY_ENGINE_VERSION = '1.4.1'
5
- ::RUBY_RELEASE_DATE = '2022-01-12'
4
+ ::RUBY_ENGINE_VERSION = '1.5.0.rc1'
5
+ ::RUBY_RELEASE_DATE = '2022-04-06'
6
6
  ::RUBY_PATCHLEVEL = 0
7
7
  ::RUBY_REVISION = '0'
8
- ::RUBY_COPYRIGHT = 'opal - Copyright (C) 2013-2021 Adam Beynon and the Opal contributors'
8
+ ::RUBY_COPYRIGHT = 'opal - Copyright (C) 2013-2022 Adam Beynon and the Opal contributors'
9
9
  ::RUBY_DESCRIPTION = "opal #{::RUBY_ENGINE_VERSION} (#{::RUBY_RELEASE_DATE} revision #{::RUBY_REVISION})"
data/opal/corelib/hash.rb CHANGED
@@ -423,7 +423,7 @@ class ::Hash
423
423
  return enum_for(:each) { size } unless block
424
424
 
425
425
  %x{
426
- for (var i = 0, keys = self.$$keys, length = keys.length, key, value; i < length; i++) {
426
+ for (var i = 0, keys = self.$$keys.slice(), length = keys.length, key, value; i < length; i++) {
427
427
  key = keys[i];
428
428
 
429
429
  if (key.$$is_string) {
@@ -444,7 +444,7 @@ class ::Hash
444
444
  return enum_for(:each_key) { size } unless block
445
445
 
446
446
  %x{
447
- for (var i = 0, keys = self.$$keys, length = keys.length, key; i < length; i++) {
447
+ for (var i = 0, keys = self.$$keys.slice(), length = keys.length, key; i < length; i++) {
448
448
  key = keys[i];
449
449
 
450
450
  block(key.$$is_string ? key : key.key);
@@ -0,0 +1,192 @@
1
+ # Debug is a helper module that allows us to conduct some debugging on
2
+ # a live codebase. It goes with an assumption, that opal-parser or
3
+ # opal-replutils will not be loaded, in which case we will do what we can
4
+ # to provision it.
5
+
6
+ module Opal
7
+ module IRB
8
+ def self.ensure_loaded(library)
9
+ return if `Opal.loaded_features`.include? library
10
+
11
+ version = if RUBY_ENGINE_VERSION.include? 'dev'
12
+ 'master'
13
+ else
14
+ RUBY_ENGINE_VERSION
15
+ end
16
+
17
+ url = "https://cdn.opalrb.com/opal/#{version}/#{library}.js"
18
+
19
+ %x{
20
+ var libcode;
21
+
22
+ if (typeof XMLHttpRequest !== 'undefined') { // Browser
23
+ var r = new XMLHttpRequest();
24
+ r.open("GET", url, false);
25
+ r.send('');
26
+ libcode = r.responseText;
27
+ }
28
+ else {
29
+ #{::Kernel.raise "You need to provision #{library} yourself in this environment"}
30
+ }
31
+
32
+ (new Function('Opal', libcode))(Opal);
33
+
34
+ Opal.require(library);
35
+ }
36
+
37
+ ::Kernel.raise "Could not load #{library} for some reason" unless `Opal.loaded_features`.include? library
38
+ end
39
+
40
+ singleton_class.attr_accessor :output
41
+
42
+ def self.prepare_console(&block)
43
+ self.output = ''
44
+
45
+ original = {
46
+ $stdout => ->(i) { $stdout = i },
47
+ $stderr => ->(i) { $stderr = i },
48
+ }
49
+
50
+ # Prepare a better prompt experience for a browser
51
+ if browser?
52
+ original.each do |pipe, pipe_setter|
53
+ new_pipe = pipe.dup
54
+ new_pipe.write_proc = proc do |str|
55
+ self.output += str
56
+ self.output = output.split("\n").last(30).join("\n")
57
+ self.output += "\n" if str.end_with? "\n"
58
+
59
+ pipe.write_proc.call(str)
60
+ end
61
+ new_pipe.tty = false
62
+ pipe_setter.call(new_pipe)
63
+ end
64
+
65
+ original_read_proc = $stdin.read_proc
66
+ $stdin.read_proc = `function(s) { var p = prompt(#{output}); if (p !== null) return p + "\n"; return nil; }`
67
+ end
68
+
69
+ yield
70
+ ensure
71
+ original.each do |pipe, pipe_setter|
72
+ pipe_setter.call(pipe)
73
+ end
74
+ $stdin.read_proc = original_read_proc
75
+ self.output = ''
76
+ end
77
+
78
+ def self.browser?
79
+ `typeof(document) !== 'undefined' && typeof(prompt) !== 'undefined'`
80
+ end
81
+
82
+ LINEBREAKS = [
83
+ 'unexpected token $end',
84
+ 'unterminated string meets end of file'
85
+ ].freeze
86
+
87
+ class Silencer
88
+ def initialize
89
+ @stderr = $stderr
90
+ end
91
+
92
+ def silence
93
+ @collector = ::StringIO.new
94
+ $stderr = @collector
95
+ yield
96
+ ensure
97
+ $stderr = @stderr
98
+ end
99
+
100
+ def warnings
101
+ @collector.string
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ class ::Binding
108
+ def irb
109
+ ::Opal::IRB.ensure_loaded('opal-replutils')
110
+
111
+ silencer = ::Opal::IRB::Silencer.new
112
+
113
+ ::Opal::IRB.prepare_console do
114
+ loop do
115
+ print '>> '
116
+ line = gets
117
+ break unless line
118
+ code = ''
119
+
120
+ puts line if ::Opal::IRB.browser?
121
+
122
+ if line.start_with? 'ls '
123
+ code = line[3..-1]
124
+ mode = :ls
125
+ elsif line == "ls\n"
126
+ code = 'self'
127
+ mode = :ls
128
+ elsif line.start_with? 'show '
129
+ code = line[5..-1]
130
+ mode = :show
131
+ else
132
+ code = line
133
+ mode = :inspect
134
+ end
135
+
136
+ js_code = nil
137
+
138
+ begin
139
+ silencer.silence do
140
+ js_code = `Opal.compile(code, {irb: true})`
141
+ end
142
+ rescue SyntaxError => e
143
+ if ::Opal::IRB::LINEBREAKS.include?(e.message)
144
+ print '.. '
145
+ line = gets
146
+ return unless line
147
+ puts line if ::Opal::IRB.browser?
148
+ code += line
149
+ retry
150
+ elsif silencer.warnings.empty?
151
+ warn e.full_message
152
+ else
153
+ # Most likely a parser error
154
+ warn silencer.warnings
155
+ end
156
+ end
157
+
158
+ if mode == :show
159
+ puts js_code
160
+ return
161
+ end
162
+
163
+ puts ::REPLUtils.eval_and_print(js_code, mode, false, self)
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ %x{
170
+ // Run in WebTools console with: Opal.irb(c => eval(c))
171
+ Opal.irb = function(fun) {
172
+ #{::Binding.new(`fun`).irb}
173
+ }
174
+
175
+ Opal.load_parser = function() {
176
+ Opal.Opal.IRB.$ensure_loaded('opal-parser');
177
+ }
178
+
179
+ if (typeof Opal.eval === 'undefined') {
180
+ Opal.eval = function(str) {
181
+ Opal.load_parser();
182
+ return Opal.eval(str);
183
+ }
184
+ }
185
+
186
+ if (typeof Opal.compile === 'undefined') {
187
+ Opal.compile = function(str, options) {
188
+ Opal.load_parser();
189
+ return Opal.compile(str, options);
190
+ }
191
+ }
192
+ }
@@ -0,0 +1,127 @@
1
+ # Polyfills for browsers in the age of IE11
2
+
3
+ unless defined?(`Math.acosh`)
4
+ %x{
5
+ Math.acosh = function(x) {
6
+ return Math.log(x + Math.sqrt(x * x - 1));
7
+ }
8
+ }
9
+ end
10
+
11
+ unless defined?(`Math.asinh`)
12
+ %x{
13
+ Math.asinh = function(x) {
14
+ return Math.log(x + Math.sqrt(x * x + 1))
15
+ }
16
+ }
17
+ end
18
+
19
+ unless defined?(`Math.atanh`)
20
+ %x{
21
+ Math.atanh = function(x) {
22
+ return 0.5 * Math.log((1 + x) / (1 - x));
23
+ }
24
+ }
25
+ end
26
+
27
+ unless defined?(`Math.cbrt`)
28
+ %x{
29
+ Math.cbrt = function(x) {
30
+ if (x == 0) {
31
+ return 0;
32
+ }
33
+
34
+ if (x < 0) {
35
+ return -Math.cbrt(-x);
36
+ }
37
+
38
+ var r = x,
39
+ ex = 0;
40
+
41
+ while (r < 0.125) {
42
+ r *= 8;
43
+ ex--;
44
+ }
45
+
46
+ while (r > 1.0) {
47
+ r *= 0.125;
48
+ ex++;
49
+ }
50
+
51
+ r = (-0.46946116 * r + 1.072302) * r + 0.3812513;
52
+
53
+ while (ex < 0) {
54
+ r *= 0.5;
55
+ ex++;
56
+ }
57
+
58
+ while (ex > 0) {
59
+ r *= 2;
60
+ ex--;
61
+ }
62
+
63
+ r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
64
+ r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
65
+ r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
66
+ r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
67
+
68
+ return r;
69
+ }
70
+ }
71
+ end
72
+
73
+ unless defined?(`Math.cosh`)
74
+ %x{
75
+ Math.cosh = function(x) {
76
+ return (Math.exp(x) + Math.exp(-x)) / 2;
77
+ }
78
+ }
79
+ end
80
+
81
+ unless defined?(`Math.hypot`)
82
+ %x{
83
+ Math.hypot = function(x, y) {
84
+ return Math.sqrt(x * x + y * y)
85
+ }
86
+ }
87
+ end
88
+
89
+ unless defined?(`Math.log2`)
90
+ %x{
91
+ Math.log2 = function(x) {
92
+ return Math.log(x) / Math.LN2;
93
+ }
94
+ }
95
+ end
96
+
97
+ unless defined?(`Math.log10`)
98
+ %x{
99
+ Math.log10 = function(x) {
100
+ return Math.log(x) / Math.LN10;
101
+ }
102
+ }
103
+ end
104
+
105
+ unless defined?(`Math.sinh`)
106
+ %x{
107
+ Math.sinh = function(x) {
108
+ return (Math.exp(x) - Math.exp(-x)) / 2;
109
+ }
110
+ }
111
+ end
112
+
113
+ unless defined?(`Math.tanh`)
114
+ %x{
115
+ Math.tanh = function(x) {
116
+ if (x == Infinity) {
117
+ return 1;
118
+ }
119
+ else if (x == -Infinity) {
120
+ return -1;
121
+ }
122
+ else {
123
+ return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x));
124
+ }
125
+ }
126
+ }
127
+ end