opal 1.1.1 → 1.2.0.beta1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/build.yml +11 -5
  6. data/.gitignore +1 -0
  7. data/.jshintrc +1 -1
  8. data/Gemfile +0 -4
  9. data/HACKING.md +1 -1
  10. data/README.md +19 -15
  11. data/UNRELEASED.md +41 -0
  12. data/benchmark-ips/bm_array_unshift.rb +7 -0
  13. data/bin/opal-mspec +2 -0
  14. data/docs/compiler.md +1 -1
  15. data/examples/rack/Gemfile +0 -1
  16. data/examples/rack/Gemfile.lock +0 -4
  17. data/lib/opal/cli.rb +1 -0
  18. data/lib/opal/cli_options.rb +4 -0
  19. data/lib/opal/cli_runners/nodejs.rb +4 -0
  20. data/lib/opal/cli_runners/source-map-support-browser.js +3 -1
  21. data/lib/opal/cli_runners/source-map-support-node.js +3 -1
  22. data/lib/opal/cli_runners/source-map-support.js +3 -1
  23. data/lib/opal/compiler.rb +2 -2
  24. data/lib/opal/nodes/args/arity_check.rb +1 -0
  25. data/lib/opal/nodes/args/parameters.rb +6 -0
  26. data/lib/opal/nodes/class.rb +1 -13
  27. data/lib/opal/nodes/literal.rb +14 -7
  28. data/lib/opal/nodes/module.rb +13 -9
  29. data/lib/opal/nodes/variables.rb +13 -4
  30. data/lib/opal/nodes/while.rb +54 -17
  31. data/lib/opal/parser.rb +1 -5
  32. data/lib/opal/parser/patch.rb +34 -0
  33. data/lib/opal/repl.rb +7 -0
  34. data/lib/opal/rewriter.rb +2 -0
  35. data/lib/opal/rewriters/arguments.rb +4 -1
  36. data/lib/opal/rewriters/forward_args.rb +54 -0
  37. data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
  38. data/lib/opal/rewriters/opal_engine_check.rb +5 -7
  39. data/lib/opal/version.rb +1 -1
  40. data/opal/corelib/array.rb +42 -20
  41. data/opal/corelib/array/pack.rb +6 -1
  42. data/opal/corelib/complex.rb +2 -0
  43. data/opal/corelib/constants.rb +3 -3
  44. data/opal/corelib/hash.rb +36 -38
  45. data/opal/corelib/module.rb +2 -7
  46. data/opal/corelib/number.rb +2 -180
  47. data/opal/corelib/numeric.rb +156 -0
  48. data/opal/corelib/object_space.rb +102 -0
  49. data/opal/corelib/random.rb +31 -66
  50. data/opal/corelib/random/formatter.rb +122 -0
  51. data/opal/corelib/range.rb +50 -19
  52. data/opal/corelib/runtime.js +82 -21
  53. data/opal/corelib/string.rb +86 -52
  54. data/opal/corelib/string/encoding.rb +140 -25
  55. data/opal/corelib/string/unpack.rb +26 -40
  56. data/opal/opal.rb +1 -0
  57. data/opal/opal/full.rb +1 -0
  58. data/spec/filters/bugs/array.rb +0 -22
  59. data/spec/filters/bugs/basicobject.rb +3 -0
  60. data/spec/filters/bugs/encoding.rb +0 -2
  61. data/spec/filters/bugs/exception.rb +1 -0
  62. data/spec/filters/bugs/float.rb +0 -2
  63. data/spec/filters/bugs/hash.rb +2 -7
  64. data/spec/filters/bugs/integer.rb +0 -2
  65. data/spec/filters/bugs/kernel.rb +16 -3
  66. data/spec/filters/bugs/language.rb +6 -14
  67. data/spec/filters/bugs/marshal.rb +1 -3
  68. data/spec/filters/bugs/module.rb +16 -1
  69. data/spec/filters/bugs/numeric.rb +4 -12
  70. data/spec/filters/bugs/objectspace.rb +67 -0
  71. data/spec/filters/bugs/pack_unpack.rb +0 -9
  72. data/spec/filters/bugs/pathname.rb +1 -0
  73. data/spec/filters/bugs/proc.rb +8 -0
  74. data/spec/filters/bugs/random.rb +3 -6
  75. data/spec/filters/bugs/range.rb +83 -113
  76. data/spec/filters/bugs/set.rb +2 -0
  77. data/spec/filters/bugs/string.rb +31 -70
  78. data/spec/filters/bugs/struct.rb +2 -0
  79. data/spec/filters/bugs/time.rb +8 -2
  80. data/spec/filters/unsupported/float.rb +3 -0
  81. data/spec/filters/unsupported/freeze.rb +1 -0
  82. data/spec/filters/unsupported/integer.rb +3 -0
  83. data/spec/filters/unsupported/refinements.rb +5 -0
  84. data/spec/filters/unsupported/string.rb +100 -95
  85. data/spec/filters/unsupported/time.rb +4 -0
  86. data/spec/lib/compiler_spec.rb +16 -0
  87. data/spec/lib/rewriters/forward_args_spec.rb +61 -0
  88. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
  89. data/spec/lib/rewriters/numblocks_spec.rb +44 -0
  90. data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
  91. data/spec/opal/core/language/forward_args_spec.rb +53 -0
  92. data/spec/opal/core/language/infinite_range_spec.rb +13 -0
  93. data/spec/opal/core/language/memoization_spec.rb +16 -0
  94. data/spec/opal/core/module_spec.rb +38 -2
  95. data/spec/opal/core/number/to_i_spec.rb +28 -0
  96. data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
  97. data/spec/opal/core/runtime/constants_spec.rb +20 -1
  98. data/spec/opal/core/string/subclassing_spec.rb +16 -0
  99. data/spec/opal/core/string/unpack_spec.rb +22 -0
  100. data/spec/opal/core/string_spec.rb +4 -4
  101. data/spec/ruby_specs +4 -1
  102. data/stdlib/json.rb +3 -1
  103. data/stdlib/securerandom.rb +55 -35
  104. data/tasks/testing.rake +6 -3
  105. data/test/nodejs/test_string.rb +25 -0
  106. data/vendored-minitest/minitest/assertions.rb +2 -0
  107. metadata +31 -10
  108. data/lib/opal/parser/with_c_lexer.rb +0 -15
@@ -145,6 +145,162 @@ class Numeric
145
145
  to_f.round(digits)
146
146
  end
147
147
 
148
+ def step(limit = undefined, step = undefined, to: undefined, by: undefined, &block)
149
+ %x{
150
+ if (limit !== undefined && to !== undefined) {
151
+ #{raise ArgumentError, 'to is given twice'}
152
+ }
153
+
154
+ if (step !== undefined && by !== undefined) {
155
+ #{raise ArgumentError, 'step is given twice'}
156
+ }
157
+
158
+ if (to !== undefined) {
159
+ limit = to;
160
+ }
161
+
162
+ if (by !== undefined) {
163
+ step = by;
164
+ }
165
+
166
+ if (limit === undefined) {
167
+ limit = nil;
168
+ }
169
+
170
+ function validateParameters() {
171
+ if (step === nil) {
172
+ #{raise TypeError, 'step must be numeric'}
173
+ }
174
+
175
+ if (step != null && #{step == 0}) {
176
+ #{raise ArgumentError, "step can't be 0"}
177
+ }
178
+
179
+ if (step === nil || step == null) {
180
+ step = 1;
181
+ }
182
+
183
+ var sign = #{step <=> 0};
184
+
185
+ if (sign === nil) {
186
+ #{raise ArgumentError, "0 can't be coerced into #{step.class}"}
187
+ }
188
+
189
+ if (limit === nil || limit == null) {
190
+ limit = sign > 0 ? #{Float::INFINITY} : #{-Float::INFINITY};
191
+ }
192
+
193
+ #{Opal.compare(self, limit)}
194
+ }
195
+
196
+ function stepFloatSize() {
197
+ if ((step > 0 && self > limit) || (step < 0 && self < limit)) {
198
+ return 0;
199
+ } else if (step === Infinity || step === -Infinity) {
200
+ return 1;
201
+ } else {
202
+ var abs = Math.abs, floor = Math.floor,
203
+ err = (abs(self) + abs(limit) + abs(limit - self)) / abs(step) * #{Float::EPSILON};
204
+
205
+ if (err === Infinity || err === -Infinity) {
206
+ return 0;
207
+ } else {
208
+ if (err > 0.5) {
209
+ err = 0.5;
210
+ }
211
+
212
+ return floor((limit - self) / step + err) + 1
213
+ }
214
+ }
215
+ }
216
+
217
+ function stepSize() {
218
+ validateParameters();
219
+
220
+ if (step === 0) {
221
+ return Infinity;
222
+ }
223
+
224
+ if (step % 1 !== 0) {
225
+ return stepFloatSize();
226
+ } else if ((step > 0 && self > limit) || (step < 0 && self < limit)) {
227
+ return 0;
228
+ } else {
229
+ var ceil = Math.ceil, abs = Math.abs,
230
+ lhs = abs(self - limit) + 1,
231
+ rhs = abs(step);
232
+
233
+ return ceil(lhs / rhs);
234
+ }
235
+ }
236
+
237
+ }
238
+
239
+ return enum_for(:step, limit, step, &`stepSize`) unless block_given?
240
+
241
+ %x{
242
+ validateParameters();
243
+
244
+ var isDesc = #{step.negative?},
245
+ isInf = #{step == 0} ||
246
+ (limit === Infinity && !isDesc) ||
247
+ (limit === -Infinity && isDesc);
248
+
249
+ if (self.$$is_number && step.$$is_number && limit.$$is_number) {
250
+ if (self % 1 === 0 && (isInf || limit % 1 === 0) && step % 1 === 0) {
251
+ var value = self;
252
+
253
+ if (isInf) {
254
+ for (;; value += step) {
255
+ block(value);
256
+ }
257
+ } else if (isDesc) {
258
+ for (; value >= limit; value += step) {
259
+ block(value);
260
+ }
261
+ } else {
262
+ for (; value <= limit; value += step) {
263
+ block(value);
264
+ }
265
+ }
266
+
267
+ return self;
268
+ } else {
269
+ var begin = #{to_f}.valueOf();
270
+ step = #{step.to_f}.valueOf();
271
+ limit = #{limit.to_f}.valueOf();
272
+
273
+ var n = stepFloatSize();
274
+
275
+ if (!isFinite(step)) {
276
+ if (n !== 0) block(begin);
277
+ } else if (step === 0) {
278
+ while (true) {
279
+ block(begin);
280
+ }
281
+ } else {
282
+ for (var i = 0; i < n; i++) {
283
+ var d = i * step + self;
284
+ if (step >= 0 ? limit < d : limit > d) {
285
+ d = limit;
286
+ }
287
+ block(d);
288
+ }
289
+ }
290
+
291
+ return self;
292
+ }
293
+ }
294
+ }
295
+
296
+ counter = self
297
+
298
+ while `isDesc ? #{counter >= limit} : #{counter <= limit}`
299
+ yield counter
300
+ counter += step
301
+ end
302
+ end
303
+
148
304
  def to_c
149
305
  Complex(self, 0)
150
306
  end
@@ -0,0 +1,102 @@
1
+ # helpers: respond_to, falsy, truthy
2
+
3
+ module ObjectSpace
4
+ module_function
5
+
6
+ %x{
7
+ var callers = {}, registry, add_caller, delete_callers;
8
+ if (typeof FinalizationRegistry === "function") {
9
+ registry = new FinalizationRegistry(function(id) {
10
+ if (typeof callers[id] !== "undefined") {
11
+ for (var i = 0; i < callers[id].length; i++) {
12
+ #{`callers[id][i]`.call(`id`)};
13
+ }
14
+ delete callers[id];
15
+ }
16
+ });
17
+ add_caller = function(id, value) {
18
+ callers[id] = callers[id] || [];
19
+ callers[id].push(value);
20
+ }
21
+ delete_callers = function(id) {
22
+ delete callers[id];
23
+ }
24
+ }
25
+ else {
26
+ // A weak polyfill for FinalizationRegistry
27
+ registry = {
28
+ register: function(){},
29
+ unregister: function(){}
30
+ };
31
+ add_caller = function(){};
32
+ delete_callers = function(){};
33
+ }
34
+ }
35
+
36
+ def define_finalizer(obj, aproc = undefined, &block)
37
+ %x{
38
+ if ($truthy(block)) aproc = block;
39
+ if ($falsy(aproc)) aproc = #{proc};
40
+ if (!$respond_to(aproc, '$call')) {
41
+ #{raise ArgumentError, "Wrong type argument #{aproc.class} (should be callable)"};
42
+ }
43
+ var id = #{obj.__id__};
44
+ add_caller(id, aproc);
45
+ try {
46
+ registry.register(obj, id, obj);
47
+ }
48
+ catch (e) {
49
+ delete_callers(id);
50
+ #{raise ArgumentError, "cannot define finalizer for #{obj.class}"};
51
+ }
52
+ return [0, aproc];
53
+ }
54
+ end
55
+
56
+ def undefine_finalizer(obj)
57
+ %{
58
+ var id = #{obj.__id__};
59
+ registry.unregister(obj);
60
+ delete_callers(id);
61
+ return obj;
62
+ }
63
+ end
64
+
65
+ class WeakMap
66
+ include Enumerable
67
+
68
+ def initialize
69
+ @weak_map = `new WeakMap()`
70
+ @primitive_map = {}
71
+ end
72
+
73
+ def [](p1)
74
+ %x{
75
+ if (typeof p1 !== "function" && typeof p1 !== "object") return #{@primitive_map[p1]};
76
+ return #{@weak_map}.get(p1);
77
+ }
78
+ end
79
+
80
+ def []=(p1, p2)
81
+ %x{
82
+ if (typeof p1 !== "function" && typeof p1 !== "object") return #{@primitive_map[p1] = p2};
83
+ return #{@weak_map}.set(p1, p2);
84
+ }
85
+ end
86
+
87
+ def include?(p1)
88
+ %x{
89
+ if (typeof p1 !== "function" && typeof p1 !== "object") return #{@primitive_map.key? p1};
90
+ return #{@weak_map}.has(p1);
91
+ }
92
+ end
93
+ alias member? include?
94
+ alias key? include?
95
+
96
+ %i[each each_key each_value each_pair keys values size length].each do |i|
97
+ define_method i do |*|
98
+ raise NotImplementedError, "##{i} can't be implemented on top of JS interfaces"
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,6 +1,18 @@
1
+ # helpers: falsy
2
+
1
3
  class Random
2
4
  attr_reader :seed, :state
3
5
 
6
+ def self._verify_count(count)
7
+ %x{
8
+ if ($falsy(count)) count = 16;
9
+ if (typeof count !== "number") count = #{`count`.to_int};
10
+ if (count < 0) #{raise ArgumentError, 'negative string size (or size too big)'};
11
+ count = Math.floor(count);
12
+ return count;
13
+ }
14
+ end
15
+
4
16
  def initialize(seed = Random.new_seed)
5
17
  seed = Opal.coerce_to!(seed, Integer, :to_int)
6
18
  @state = seed
@@ -29,13 +41,7 @@ class Random
29
41
  end
30
42
 
31
43
  def self.urandom(size)
32
- size = Opal.coerce_to!(size, Integer, :to_int)
33
-
34
- if size < 0
35
- raise ArgumentError, 'negative string size (or size too big)'
36
- end
37
-
38
- Array.new(size) { rand(255).chr }.join.encode('ASCII-8BIT')
44
+ ::SecureRandom.bytes(size)
39
45
  end
40
46
 
41
47
  def ==(other)
@@ -45,74 +51,33 @@ class Random
45
51
  end
46
52
 
47
53
  def bytes(length)
48
- length = Opal.coerce_to!(length, Integer, :to_int)
54
+ length = Random._verify_count(length)
49
55
 
50
56
  Array.new(length) { rand(255).chr }.join.encode('ASCII-8BIT')
51
57
  end
52
58
 
59
+ def self.bytes(length)
60
+ DEFAULT.bytes(length)
61
+ end
62
+
53
63
  def rand(limit = undefined)
64
+ random_number(limit)
65
+ end
66
+
67
+ # Not part of the Ruby interface (use #random_number for portability), but
68
+ # used by Random::Formatter as a shortcut, as for Random interface the float
69
+ # RNG is primary.
70
+ def random_float
54
71
  %x{
55
- function randomFloat() {
56
- self.state++;
57
- return Opal.$$rand.rand(self.$rng);
58
- }
59
-
60
- function randomInt() {
61
- return Math.floor(randomFloat() * limit);
62
- }
63
-
64
- function randomRange() {
65
- var min = limit.begin,
66
- max = limit.end;
67
-
68
- if (min === nil || max === nil) {
69
- return nil;
70
- }
71
-
72
- var length = max - min;
73
-
74
- if (length < 0) {
75
- return nil;
76
- }
77
-
78
- if (length === 0) {
79
- return min;
80
- }
81
-
82
- if (max % 1 === 0 && min % 1 === 0 && !limit.excl) {
83
- length++;
84
- }
85
-
86
- return self.$rand(length) + min;
87
- }
88
-
89
- if (limit == null) {
90
- return randomFloat();
91
- } else if (limit.$$is_range) {
92
- return randomRange();
93
- } else if (limit.$$is_number) {
94
- if (limit <= 0) {
95
- #{raise ArgumentError, "invalid argument - #{limit}"}
96
- }
97
-
98
- if (limit % 1 === 0) {
99
- // integer
100
- return randomInt();
101
- } else {
102
- return randomFloat() * limit;
103
- }
104
- } else {
105
- limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
106
-
107
- if (limit <= 0) {
108
- #{raise ArgumentError, "invalid argument - #{limit}"}
109
- }
110
-
111
- return randomInt();
112
- }
72
+ self.state++;
73
+ return Opal.$$rand.rand(self.$rng);
113
74
  }
114
75
  end
115
76
 
77
+ def self.random_float
78
+ DEFAULT.random_float
79
+ end
80
+
116
81
  def self.generator=(generator)
117
82
  `Opal.$$rand = #{generator}`
118
83
 
@@ -0,0 +1,122 @@
1
+ class Random
2
+ module Formatter
3
+ def hex(count = nil)
4
+ count = Random._verify_count(count)
5
+ %x{
6
+ var bytes = #{bytes(count)};
7
+ var out = "";
8
+ for (var i = 0; i < #{count}; i++) {
9
+ out += bytes.charCodeAt(i).toString(16).padStart(2, '0');
10
+ }
11
+ return #{`out`.encode('US-ASCII')};
12
+ }
13
+ end
14
+
15
+ def random_bytes(count = nil)
16
+ bytes(count)
17
+ end
18
+
19
+ def base64(count = nil)
20
+ Base64.strict_encode64(random_bytes(count)).encode('US-ASCII')
21
+ end
22
+
23
+ def urlsafe_base64(count = nil, padding = false)
24
+ Base64.urlsafe_encode64(random_bytes(count), padding).encode('US-ASCII')
25
+ end
26
+
27
+ def uuid
28
+ str = hex(16).split('')
29
+ str[12] = '4'
30
+ str[16] = `(parseInt(#{str[16]}, 16) & 3 | 8).toString(16)`
31
+ str = [str[0...8], str[8...12], str[12...16], str[16...20], str[20...32]]
32
+ str = str.map(&:join)
33
+ str.join('-')
34
+ end
35
+
36
+ # Implemented in terms of `#bytes` for SecureRandom, but Random overrides this
37
+ # method to implement `#bytes` in terms of `#random_float`. Not part of standard
38
+ # Ruby interface - use random_number for portability.
39
+ def random_float
40
+ bs = bytes(4)
41
+ num = 0
42
+ 4.times do |i|
43
+ num <<= 8
44
+ num |= bs[i].ord
45
+ end
46
+ num.abs / 0x7fffffff
47
+ end
48
+
49
+ def random_number(limit = undefined)
50
+ %x{
51
+ function randomFloat() {
52
+ return #{random_float};
53
+ }
54
+
55
+ function randomInt(max) {
56
+ return Math.floor(randomFloat() * max);
57
+ }
58
+
59
+ function randomRange() {
60
+ var min = limit.begin,
61
+ max = limit.end;
62
+
63
+ if (min === nil || max === nil) {
64
+ return nil;
65
+ }
66
+
67
+ var length = max - min;
68
+
69
+ if (length < 0) {
70
+ return nil;
71
+ }
72
+
73
+ if (length === 0) {
74
+ return min;
75
+ }
76
+
77
+ if (max % 1 === 0 && min % 1 === 0 && !limit.excl) {
78
+ length++;
79
+ }
80
+
81
+ return randomInt(length) + min;
82
+ }
83
+
84
+ if (limit == null) {
85
+ return randomFloat();
86
+ } else if (limit.$$is_range) {
87
+ return randomRange();
88
+ } else if (limit.$$is_number) {
89
+ if (limit <= 0) {
90
+ #{raise ArgumentError, "invalid argument - #{limit}"}
91
+ }
92
+
93
+ if (limit % 1 === 0) {
94
+ // integer
95
+ return randomInt(limit);
96
+ } else {
97
+ return randomFloat() * limit;
98
+ }
99
+ } else {
100
+ limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
101
+
102
+ if (limit <= 0) {
103
+ #{raise ArgumentError, "invalid argument - #{limit}"}
104
+ }
105
+
106
+ return randomInt(limit);
107
+ }
108
+ }
109
+ end
110
+
111
+ def alphanumeric(count = nil)
112
+ count = Random._verify_count(count)
113
+ map = ['0'..'9', 'a'..'z', 'A'..'Z'].map(&:to_a).flatten
114
+ Array.new(count) do |i|
115
+ map[random_number(map.length)]
116
+ end.join
117
+ end
118
+ end
119
+
120
+ include Random::Formatter
121
+ extend Random::Formatter
122
+ end