opal 1.8.0.alpha1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -0
  3. data/CHANGELOG.md +119 -2
  4. data/README.md +7 -7
  5. data/UNRELEASED.md +1 -78
  6. data/docs/releasing.md +8 -16
  7. data/lib/opal/cli_runners/chrome.rb +5 -1
  8. data/lib/opal/nodes/closure.rb +15 -7
  9. data/lib/opal/nodes/defined.rb +1 -1
  10. data/lib/opal/nodes/literal.rb +10 -6
  11. data/lib/opal/nodes/rescue.rb +1 -1
  12. data/lib/opal/simple_server.rb +2 -2
  13. data/lib/opal/version.rb +1 -1
  14. data/opal/corelib/array.rb +39 -21
  15. data/opal/corelib/class.rb +26 -8
  16. data/opal/corelib/complex.rb +1 -1
  17. data/opal/corelib/constants.rb +2 -2
  18. data/opal/corelib/enumerator/arithmetic_sequence.rb +1 -1
  19. data/opal/corelib/error.rb +10 -0
  20. data/opal/corelib/hash.rb +36 -20
  21. data/opal/corelib/kernel.rb +0 -1
  22. data/opal/corelib/method.rb +1 -0
  23. data/opal/corelib/module.rb +82 -22
  24. data/opal/corelib/number.rb +28 -1
  25. data/opal/corelib/range.rb +55 -11
  26. data/opal/corelib/rational.rb +1 -1
  27. data/opal/corelib/runtime.js +132 -50
  28. data/opal/corelib/string.rb +36 -8
  29. data/opal/corelib/struct.rb +1 -1
  30. data/opal/corelib/time.rb +1 -1
  31. data/spec/filters/bugs/array.rb +58 -0
  32. data/spec/filters/bugs/basicobject.rb +7 -0
  33. data/spec/filters/bugs/bigdecimal.rb +1 -2
  34. data/spec/filters/bugs/binding.rb +1 -0
  35. data/spec/filters/bugs/cgi.rb +40 -0
  36. data/spec/filters/bugs/class.rb +2 -3
  37. data/spec/filters/bugs/complex.rb +3 -0
  38. data/spec/filters/bugs/date.rb +5 -2
  39. data/spec/filters/bugs/datetime.rb +1 -0
  40. data/spec/filters/bugs/delegate.rb +1 -2
  41. data/spec/filters/bugs/encoding.rb +1 -1
  42. data/spec/filters/bugs/enumerable.rb +11 -0
  43. data/spec/filters/bugs/enumerator.rb +15 -2
  44. data/spec/filters/bugs/exception.rb +9 -4
  45. data/spec/filters/bugs/file.rb +2 -0
  46. data/spec/filters/bugs/float.rb +1 -0
  47. data/spec/filters/bugs/freeze.rb +5 -49
  48. data/spec/filters/bugs/hash.rb +1 -0
  49. data/spec/filters/bugs/integer.rb +5 -6
  50. data/spec/filters/bugs/kernel.rb +12 -6
  51. data/spec/filters/bugs/language.rb +33 -15
  52. data/spec/filters/bugs/marshal.rb +63 -4
  53. data/spec/filters/bugs/method.rb +2 -13
  54. data/spec/filters/bugs/module.rb +19 -17
  55. data/spec/filters/bugs/objectspace.rb +2 -0
  56. data/spec/filters/bugs/pathname.rb +1 -0
  57. data/spec/filters/bugs/proc.rb +4 -2
  58. data/spec/filters/bugs/random.rb +0 -3
  59. data/spec/filters/bugs/range.rb +3 -17
  60. data/spec/filters/bugs/rational.rb +2 -0
  61. data/spec/filters/bugs/refinement.rb +19 -0
  62. data/spec/filters/bugs/regexp.rb +27 -5
  63. data/spec/filters/bugs/ruby-32.rb +0 -4
  64. data/spec/filters/bugs/set.rb +10 -2
  65. data/spec/filters/bugs/singleton.rb +0 -2
  66. data/spec/filters/bugs/string.rb +140 -1
  67. data/spec/filters/bugs/struct.rb +15 -5
  68. data/spec/filters/bugs/time.rb +56 -2
  69. data/spec/filters/bugs/trace_point.rb +1 -0
  70. data/spec/filters/bugs/unboundmethod.rb +4 -9
  71. data/spec/filters/bugs/warnings.rb +0 -1
  72. data/spec/filters/platform/firefox/exception.rb +3 -3
  73. data/spec/filters/platform/firefox/kernel.rb +1 -0
  74. data/spec/filters/platform/safari/exception.rb +2 -2
  75. data/spec/filters/platform/safari/float.rb +1 -0
  76. data/spec/filters/platform/safari/kernel.rb +1 -0
  77. data/spec/filters/platform/safari/literal_regexp.rb +2 -2
  78. data/spec/filters/unsupported/hash.rb +1 -1
  79. data/spec/lib/compiler_spec.rb +4 -0
  80. data/spec/opal/core/class/clone_spec.rb +36 -0
  81. data/spec/opal/core/module/define_method_spec.rb +29 -0
  82. data/spec/opal/core/object_id_spec.rb +0 -6
  83. data/spec/opal/language/predefined_spec.rb +20 -0
  84. data/spec/opal/language/yield_spec.rb +43 -0
  85. data/spec/ruby_specs +1 -2
  86. data/stdlib/bigdecimal.rb +2 -0
  87. data/stdlib/cgi/util.rb +89 -0
  88. data/stdlib/cgi.rb +1 -14
  89. data/stdlib/delegate.rb +3 -4
  90. data/stdlib/pathname.rb +1 -1
  91. data/stdlib/promise/v2.rb +22 -7
  92. data/stdlib/stringio.rb +2 -0
  93. data/tasks/testing.rake +15 -11
  94. data/test/opal/promisev2/test_always.rb +14 -0
  95. data/test/opal/unsupported_and_bugs.rb +0 -8
  96. metadata +16 -8
@@ -1,8 +1,8 @@
1
1
  ::RUBY_PLATFORM = 'opal'
2
2
  ::RUBY_ENGINE = 'opal'
3
3
  ::RUBY_VERSION = '3.2.0'
4
- ::RUBY_ENGINE_VERSION = '1.8.0.alpha1'
5
- ::RUBY_RELEASE_DATE = '2023-08-04'
4
+ ::RUBY_ENGINE_VERSION = '1.8.0'
5
+ ::RUBY_RELEASE_DATE = '2023-10-26'
6
6
  ::RUBY_PATCHLEVEL = 0
7
7
  ::RUBY_REVISION = '0'
8
8
  ::RUBY_COPYRIGHT = 'opal - Copyright (C) 2011-2023 Adam Beynon and the Opal contributors'
@@ -153,7 +153,7 @@ class ::Enumerator
153
153
  end
154
154
 
155
155
  def hash
156
- [self.begin, self.end, step, exclude_end?].hash
156
+ [ArithmeticSequence, self.begin, self.end, step, exclude_end?].hash
157
157
  end
158
158
 
159
159
  def inspect
@@ -316,6 +316,16 @@ class ::KeyError
316
316
  end
317
317
  end
318
318
 
319
+ class ::LocalJumpError
320
+ attr_reader :exit_value, :reason
321
+
322
+ def initialize(message, exit_value = nil, reason = :noreason)
323
+ super message
324
+ @exit_value = exit_value
325
+ @reason = reason
326
+ end
327
+ end
328
+
319
329
  module ::JS
320
330
  class Error
321
331
  end
data/opal/corelib/hash.rb CHANGED
@@ -1,4 +1,4 @@
1
- # helpers: yield1, hash_clone, hash_delete, hash_each, hash_get, hash_put, deny_frozen_access, freeze
1
+ # helpers: yield1, hash_clone, hash_delete, hash_each, hash_get, hash_put, deny_frozen_access, freeze, opal32_init, opal32_add
2
2
  # backtick_javascript: true
3
3
 
4
4
  require 'corelib/enumerable'
@@ -210,7 +210,8 @@ class ::Hash < `Map`
210
210
  def clone
211
211
  %x{
212
212
  var hash = self.$class().$new();
213
- return $hash_clone(self, hash);
213
+ $hash_clone(self, hash);
214
+ return self["$frozen?"]() ? hash.$freeze() : hash;
214
215
  }
215
216
  end
216
217
 
@@ -365,6 +366,10 @@ class ::Hash < `Map`
365
366
  item.dig(*keys)
366
367
  end
367
368
 
369
+ def dup
370
+ `$hash_clone(self, self.$class().$new())`
371
+ end
372
+
368
373
  def each(&block)
369
374
  return enum_for(:each) { size } unless block
370
375
 
@@ -481,41 +486,53 @@ class ::Hash < `Map`
481
486
  }
482
487
  end
483
488
 
489
+ `var $hash_ids`
490
+
484
491
  def hash
485
492
  %x{
486
- var top = (Opal.hash_ids === undefined),
493
+ var top = ($hash_ids === undefined),
487
494
  hash_id = self.$object_id(),
488
- result = ['Hash'],
489
- key, item;
495
+ result = $opal32_init(),
496
+ key, item, i,
497
+ size = self.size, ary = new Int32Array(size);
490
498
 
491
- try {
492
- if (top) {
493
- Opal.hash_ids = Object.create(null);
494
- }
499
+ result = $opal32_add(result, 0x4);
500
+ result = $opal32_add(result, size);
495
501
 
496
- if (Opal[hash_id]) {
497
- return 'self';
498
- }
502
+ if (top) {
503
+ $hash_ids = Object.create(null);
504
+ }
505
+ else if ($hash_ids[hash_id]) {
506
+ return $opal32_add(result, 0x01010101);
507
+ }
499
508
 
500
- for (key in Opal.hash_ids) {
501
- item = Opal.hash_ids[key];
509
+ try {
510
+ for (key in $hash_ids) {
511
+ item = $hash_ids[key];
502
512
  if (#{eql?(`item`)}) {
503
- return 'self';
513
+ return $opal32_add(result, 0x01010101);
504
514
  }
505
515
  }
506
516
 
507
- Opal.hash_ids[hash_id] = self;
517
+ $hash_ids[hash_id] = self;
518
+ i = 0
508
519
 
509
520
  $hash_each(self, false, function(key, value) {
510
- result.push([key, value.$hash()]);
521
+ ary[i] = [0x70414952, key, value].$hash();
522
+ i++;
511
523
  return [false, false];
512
524
  });
513
525
 
514
- return result.sort().join();
526
+ ary = ary.sort();
527
+
528
+ for (i = 0; i < ary.length; i++) {
529
+ result = $opal32_add(result, ary[i]);
530
+ }
515
531
 
532
+ return result;
516
533
  } finally {
517
534
  if (top) {
518
- Opal.hash_ids = undefined;
535
+ $hash_ids = undefined;
519
536
  }
520
537
  }
521
538
  }
@@ -920,7 +937,6 @@ class ::Hash < `Map`
920
937
  `Array.from(self.values())`
921
938
  end
922
939
 
923
- alias dup clone
924
940
  alias each_pair each
925
941
  alias eql? ==
926
942
  alias filter select
@@ -687,7 +687,6 @@ module ::Kernel
687
687
  }
688
688
 
689
689
  #{$!} = exception;
690
- #{$@} = #{`exception`.backtrace};
691
690
 
692
691
  throw exception;
693
692
  }
@@ -77,6 +77,7 @@ class ::UnboundMethod
77
77
  @owner = owner
78
78
  @method = method
79
79
  @name = name
80
+ `self.$$method = method`
80
81
  end
81
82
 
82
83
  def arity
@@ -2,6 +2,22 @@
2
2
  # backtick_javascript: true
3
3
 
4
4
  class ::Module
5
+ %x{
6
+ function ensure_symbol_or_string(name) {
7
+ if (name.$$is_string) {
8
+ return name;
9
+ };
10
+ var converted_name = #{::Opal.try_convert(`name`, ::String, :to_str)};
11
+ if (converted_name.$$is_string) {
12
+ return converted_name;
13
+ } else if (converted_name === nil) {
14
+ #{::Kernel.raise ::TypeError, "#{`name`} is not a symbol nor a string"}
15
+ } else {
16
+ #{::Kernel.raise ::TypeError, "can't convert #{`name`.class} to String (#{`name`.class}#to_str gives #{`converted_name`.class}"}
17
+ }
18
+ }
19
+ }
20
+
5
21
  def self.allocate
6
22
  %x{
7
23
  var module = Opal.allocate_module(nil, function(){});
@@ -388,9 +404,12 @@ class ::Module
388
404
 
389
405
  if (method === undefined && block === nil)
390
406
  #{::Kernel.raise ::ArgumentError, 'tried to create a Proc object without a block'}
407
+
408
+ name = ensure_symbol_or_string(name);
391
409
  }
392
410
 
393
- block ||= case method
411
+ if `method !== undefined`
412
+ block = case method
394
413
  when ::Proc
395
414
  method
396
415
 
@@ -398,15 +417,21 @@ class ::Module
398
417
  `#{method.to_proc}.$$unbound`
399
418
 
400
419
  when ::UnboundMethod
401
- ->(*args) {
402
- bound = method.bind(self)
403
- bound.call(*args)
404
- }
420
+ `Opal.wrap_method_body(method.$$method)`
405
421
 
406
422
  else
407
- ::Kernel.raise ::TypeError, "wrong argument type #{block.class} (expected Proc/Method)"
423
+ ::Kernel.raise ::TypeError, "wrong argument type #{method.class} (expected Proc/Method/UnboundMethod)"
408
424
  end
409
425
 
426
+ if `!method.$$is_proc`
427
+ owner = method.owner
428
+ if `owner.$$is_class` && !(self <= owner) # rubocop:disable Style/InverseMethods
429
+ message = `owner.$$is_singleton` ? "can't bind singleton method to a different class" : "bind argument must be a subclass of #{owner}"
430
+ ::Kernel.raise ::TypeError, message
431
+ end
432
+ end
433
+ end
434
+
410
435
  %x{
411
436
  if (typeof(Proxy) !== 'undefined') {
412
437
  var meta = Object.create(null)
@@ -414,15 +439,17 @@ class ::Module
414
439
  block.$$proxy_target = block
415
440
  block = new Proxy(block, {
416
441
  apply: function(target, self, args) {
417
- var old_name = target.$$jsid
442
+ var old_name = target.$$jsid, old_lambda = target.$$is_lambda;
418
443
  target.$$jsid = name;
444
+ target.$$is_lambda = true;
419
445
  try {
420
446
  return target.apply(self, args);
421
447
  } catch(e) {
422
448
  if (e === target.$$brk || e === target.$$ret) return e.$v;
423
449
  throw e;
424
450
  } finally {
425
- target.$$jsid = old_name
451
+ target.$$jsid = old_name;
452
+ target.$$is_lambda = old_lambda;
426
453
  }
427
454
  }
428
455
  })
@@ -453,10 +480,7 @@ class ::Module
453
480
  def remove_method(*names)
454
481
  %x{
455
482
  for (var i = 0; i < names.length; i++) {
456
- var name = names[i];
457
- if (!(typeof name === "string" || name.$$is_string)) {
458
- #{raise ::TypeError, "#{name} is not a symbol nor a string"}
459
- }
483
+ var name = ensure_symbol_or_string(names[i]);
460
484
  $deny_frozen_access(self);
461
485
 
462
486
  Opal.rdef(self, "$" + name);
@@ -705,13 +729,10 @@ class ::Module
705
729
  def undef_method(*names)
706
730
  %x{
707
731
  for (var i = 0; i < names.length; i++) {
708
- var name = names[i];
709
- if (!(typeof name === "string" || name.$$is_string)) {
710
- #{raise ::TypeError, "#{name} is not a symbol nor a string"}
711
- }
732
+ var name = ensure_symbol_or_string(names[i]);
712
733
  $deny_frozen_access(self);
713
734
 
714
- Opal.udef(self, "$" + names[i]);
735
+ Opal.udef(self, "$" + name);
715
736
  }
716
737
  }
717
738
 
@@ -733,11 +754,50 @@ class ::Module
733
754
  }
734
755
  end
735
756
 
736
- def dup
737
- copy = super
738
- copy.copy_class_variables(self)
739
- copy.copy_constants(self)
740
- copy
757
+ %x{
758
+ function copyInstanceMethods(from, to) {
759
+ var i, method_names = Opal.own_instance_methods(from);
760
+ for (i = 0; i < method_names.length; i++) {
761
+ var name = method_names[i],
762
+ jsid = $jsid(name),
763
+ body = from.$$prototype[jsid],
764
+ wrapped = Opal.wrap_method_body(body);
765
+
766
+ wrapped.$$jsid = name;
767
+ Opal.defn(to, jsid, wrapped);
768
+ }
769
+ }
770
+
771
+ function copyIncludedModules(from, to) {
772
+ var modules = from.$$own_included_modules;
773
+ for (var i = modules.length - 1; i >= 0; i--) {
774
+ Opal.append_features(modules[i], to);
775
+ }
776
+ }
777
+
778
+ function copyPrependedModules(from, to) {
779
+ var modules = from.$$own_prepended_modules;
780
+ for (var i = modules.length - 1; i >= 0; i--) {
781
+ Opal.prepend_features(modules[i], to);
782
+ }
783
+ }
784
+ }
785
+
786
+ def initialize_copy(other)
787
+ %x{
788
+ copyInstanceMethods(other, self);
789
+ copyIncludedModules(other, self);
790
+ copyPrependedModules(other, self);
791
+ self.$$cloned_from = other.$$cloned_from.concat(other);
792
+ }
793
+ copy_class_variables(other)
794
+ copy_constants(other)
795
+ end
796
+
797
+ def initialize_dup(other)
798
+ super
799
+ # Unlike other classes, Module's singleton methods are copied on Object#dup.
800
+ copy_singleton_methods(other)
741
801
  end
742
802
 
743
803
  def copy_class_variables(other)
@@ -1,4 +1,5 @@
1
1
  # backtick_javascript: true
2
+ # use_strict: true
2
3
 
3
4
  require 'corelib/numeric'
4
5
 
@@ -6,6 +7,7 @@ class ::Number < ::Numeric
6
7
  ::Opal.bridge(`Number`, self)
7
8
  `Opal.prop(self.$$prototype, '$$is_number', true)`
8
9
  `self.$$is_number_class = true`
10
+ `var number_id_map = new Map()`
9
11
 
10
12
  class << self
11
13
  def allocate
@@ -36,7 +38,32 @@ class ::Number < ::Numeric
36
38
  end
37
39
 
38
40
  def __id__
39
- `(self * 2) + 1`
41
+ %x{
42
+ // Binary-safe integers
43
+ if (self|0 === self) {
44
+ return (self * 2) + 1;
45
+ }
46
+ else {
47
+ if (number_id_map.has(self)) {
48
+ return number_id_map.get(self);
49
+ }
50
+ var id = Opal.uid();
51
+ number_id_map.set(self, id);
52
+ return id;
53
+ }
54
+ }
55
+ end
56
+
57
+ def hash
58
+ %x{
59
+ // Binary-safe integers
60
+ if (self|0 === self) {
61
+ return #{__id__}
62
+ }
63
+ else {
64
+ return self.toString().$hash();
65
+ }
66
+ }
40
67
  end
41
68
 
42
69
  def +(other)
@@ -19,7 +19,8 @@ class ::Range
19
19
  end
20
20
 
21
21
  def ===(value)
22
- include? value
22
+ return false if `value.$$is_range`
23
+ cover? value
23
24
  end
24
25
 
25
26
  %x{
@@ -44,13 +45,34 @@ class ::Range
44
45
  end
45
46
 
46
47
  def cover?(value)
47
- beg_cmp = (@begin.nil? && -1) || (@begin <=> value) || false
48
- end_cmp = (@end.nil? && -1) || (value <=> @end) || false
49
- if @excl
50
- end_cmp && end_cmp < 0
51
- else
52
- end_cmp && end_cmp <= 0
53
- end && beg_cmp && beg_cmp <= 0
48
+ compare = ->(a, b) {
49
+ a <=> b || 1
50
+ }
51
+
52
+ if `value.$$is_range`
53
+ val_begin = value.begin
54
+ val_end = value.end
55
+ val_excl = value.exclude_end?
56
+ if (@begin && val_begin.nil?) ||
57
+ (@end && val_end.nil?) ||
58
+ (val_begin && val_end && compare.call(val_begin, val_end).then { |c| val_excl ? c >= 0 : c > 0 }) ||
59
+ (val_begin && !cover?(val_begin))
60
+ return false
61
+ end
62
+
63
+ cmp = compare.call(@end, val_end)
64
+ return cmp >= 0 if @excl == val_excl
65
+ return cmp > 0 if @excl
66
+ return true if cmp >= 0
67
+
68
+ val_max = value.max
69
+ return !val_max.nil? && compare.call(val_max, @end) <= 0
70
+ end
71
+
72
+ return false if @begin && compare.call(@begin, value) > 0
73
+ return true if @end.nil?
74
+ end_cmp = compare.call(value, @end)
75
+ @excl ? end_cmp < 0 : end_cmp <= 0
54
76
  end
55
77
 
56
78
  def each(&block)
@@ -113,6 +135,29 @@ class ::Range
113
135
  super
114
136
  end
115
137
 
138
+ def include?(val)
139
+ if `self.begin.$$is_number || self.end.$$is_number` ||
140
+ @begin.is_a?(::Time) || @end.is_a?(::Time) ||
141
+ ::Integer.try_convert(@begin) || ::Integer.try_convert(@end)
142
+ return cover?(val)
143
+ end
144
+
145
+ if `self.begin.$$is_string || self.end.$$is_string`
146
+ if `self.begin.$$is_string && self.end.$$is_string`
147
+ return @begin.upto(@end, @excl).any? { |s| s == val }
148
+ elsif @begin.nil?
149
+ cmp = val <=> @end
150
+ return !cmp.nil? && (@excl ? cmp < 0 : cmp <= 0)
151
+ elsif @end.nil?
152
+ cmp = @begin <=> val
153
+ return !cmp.nil? && cmp <= 0
154
+ end
155
+ end
156
+
157
+ # invoke Enumerable#include?
158
+ super
159
+ end
160
+
116
161
  def last(n = undefined)
117
162
  ::Kernel.raise ::RangeError, 'cannot get the maximum of endless range' if @end.nil?
118
163
  return @end if `n == null`
@@ -315,10 +360,9 @@ class ::Range
315
360
  end
316
361
 
317
362
  def hash
318
- [@begin, @end, @excl].hash
363
+ [::Range, @begin, @end, @excl].hash
319
364
  end
320
365
 
321
366
  alias == eql?
322
- alias include? cover?
323
- alias member? cover?
367
+ alias member? include?
324
368
  end
@@ -244,7 +244,7 @@ class ::Rational < ::Numeric
244
244
  end
245
245
 
246
246
  def hash
247
- "Rational:#{@num}:#{@den}"
247
+ [::Rational, @num, @den].hash
248
248
  end
249
249
 
250
250
  def inspect