opal 1.4.1 → 1.5.0.rc1

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