opal 1.7.4 → 1.8.0.alpha1

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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +9 -9
  3. data/.rubocop/todo.yml +2 -2
  4. data/.rubocop.yml +17 -10
  5. data/.rubocop_todo.yml +311 -0
  6. data/CHANGELOG.md +1 -15
  7. data/UNRELEASED.md +78 -1
  8. data/benchmark-ips/bm_block_vs_yield.rb +3 -0
  9. data/benchmark-ips/bm_slice_or_not.rb +53 -0
  10. data/docs/bridging.md +112 -0
  11. data/docs/compiled_ruby.md +10 -10
  12. data/docs/getting_started.md +18 -22
  13. data/lib/opal/cli_runners/chrome.rb +1 -5
  14. data/lib/opal/cli_runners/chrome_cdp_interface.rb +1 -0
  15. data/lib/opal/cli_runners/firefox_cdp_interface.rb +1 -0
  16. data/lib/opal/compiler.rb +33 -1
  17. data/lib/opal/nodes/args/extract_kwoptarg.rb +2 -1
  18. data/lib/opal/nodes/call.rb +1 -1
  19. data/lib/opal/nodes/call_special.rb +71 -47
  20. data/lib/opal/nodes/hash.rb +14 -30
  21. data/lib/opal/nodes/if.rb +37 -29
  22. data/lib/opal/nodes/literal.rb +5 -1
  23. data/lib/opal/nodes/x_string.rb +13 -0
  24. data/lib/opal/parser/patch.rb +1 -0
  25. data/lib/opal/rewriters/for_rewriter.rb +36 -24
  26. data/lib/opal/simple_server.rb +2 -2
  27. data/lib/opal/source_map/file.rb +1 -1
  28. data/lib/opal/version.rb +1 -1
  29. data/opal/corelib/array/pack.rb +1 -0
  30. data/opal/corelib/array.rb +71 -43
  31. data/opal/corelib/basic_object.rb +1 -0
  32. data/opal/corelib/binding.rb +2 -0
  33. data/opal/corelib/boolean.rb +1 -0
  34. data/opal/corelib/class.rb +2 -0
  35. data/opal/corelib/comparable.rb +1 -0
  36. data/opal/corelib/complex.rb +2 -0
  37. data/opal/corelib/constants.rb +2 -2
  38. data/opal/corelib/dir.rb +2 -0
  39. data/opal/corelib/enumerable.rb +3 -2
  40. data/opal/corelib/enumerator/arithmetic_sequence.rb +2 -0
  41. data/opal/corelib/enumerator/chain.rb +1 -0
  42. data/opal/corelib/enumerator/generator.rb +1 -0
  43. data/opal/corelib/enumerator/lazy.rb +1 -0
  44. data/opal/corelib/enumerator/yielder.rb +2 -0
  45. data/opal/corelib/enumerator.rb +1 -0
  46. data/opal/corelib/error/errno.rb +2 -0
  47. data/opal/corelib/error.rb +12 -0
  48. data/opal/corelib/file.rb +1 -0
  49. data/opal/corelib/hash.rb +197 -504
  50. data/opal/corelib/helpers.rb +1 -0
  51. data/opal/corelib/io.rb +2 -0
  52. data/opal/corelib/irb.rb +2 -0
  53. data/opal/corelib/kernel/format.rb +1 -0
  54. data/opal/corelib/kernel.rb +70 -14
  55. data/opal/corelib/main.rb +2 -0
  56. data/opal/corelib/marshal/read_buffer.rb +2 -0
  57. data/opal/corelib/marshal/write_buffer.rb +2 -0
  58. data/opal/corelib/math/polyfills.rb +2 -0
  59. data/opal/corelib/math.rb +1 -0
  60. data/opal/corelib/method.rb +2 -0
  61. data/opal/corelib/module.rb +1 -0
  62. data/opal/corelib/nil.rb +3 -1
  63. data/opal/corelib/number.rb +2 -0
  64. data/opal/corelib/numeric.rb +2 -0
  65. data/opal/corelib/object_space.rb +1 -0
  66. data/opal/corelib/pack_unpack/format_string_parser.rb +2 -0
  67. data/opal/corelib/proc.rb +30 -28
  68. data/opal/corelib/process.rb +2 -0
  69. data/opal/corelib/random/formatter.rb +2 -0
  70. data/opal/corelib/random/math_random.js.rb +2 -0
  71. data/opal/corelib/random/mersenne_twister.rb +2 -0
  72. data/opal/corelib/random/seedrandom.js.rb +2 -0
  73. data/opal/corelib/random.rb +1 -0
  74. data/opal/corelib/range.rb +34 -12
  75. data/opal/corelib/rational.rb +2 -0
  76. data/opal/corelib/regexp.rb +1 -0
  77. data/opal/corelib/runtime.js +187 -231
  78. data/opal/corelib/set.rb +2 -0
  79. data/opal/corelib/string/encoding.rb +3 -0
  80. data/opal/corelib/string/unpack.rb +2 -0
  81. data/opal/corelib/string.rb +20 -12
  82. data/opal/corelib/struct.rb +3 -1
  83. data/opal/corelib/time.rb +1 -0
  84. data/opal/corelib/trace_point.rb +2 -0
  85. data/opal/corelib/unsupported.rb +2 -0
  86. data/opal/corelib/variables.rb +2 -0
  87. data/opal.gemspec +2 -2
  88. data/spec/filters/bugs/array.rb +0 -2
  89. data/spec/filters/bugs/enumerable.rb +0 -3
  90. data/spec/filters/bugs/hash.rb +0 -13
  91. data/spec/filters/bugs/kernel.rb +0 -38
  92. data/spec/filters/bugs/range.rb +0 -1
  93. data/spec/filters/bugs/ruby-32.rb +0 -2
  94. data/spec/filters/bugs/string.rb +0 -1
  95. data/spec/filters/bugs/struct.rb +1 -5
  96. data/spec/filters/unsupported/hash.rb +1 -0
  97. data/spec/lib/compiler_spec.rb +24 -17
  98. data/spec/mspec-opal/formatters.rb +2 -0
  99. data/spec/mspec-opal/runner.rb +2 -0
  100. data/spec/opal/core/array/dup_spec.rb +2 -0
  101. data/spec/opal/core/exception_spec.rb +2 -0
  102. data/spec/opal/core/hash/internals_spec.rb +154 -206
  103. data/spec/opal/core/hash_spec.rb +2 -0
  104. data/spec/opal/core/iterable_props_spec.rb +2 -0
  105. data/spec/opal/core/kernel/at_exit_spec.rb +2 -0
  106. data/spec/opal/core/kernel/respond_to_spec.rb +2 -0
  107. data/spec/opal/core/language/arguments/mlhs_arg_spec.rb +2 -0
  108. data/spec/opal/core/language/safe_navigator_spec.rb +2 -0
  109. data/spec/opal/core/language/xstring_send_spec.rb +15 -0
  110. data/spec/opal/core/language/xstring_spec.rb +2 -0
  111. data/spec/opal/core/language_spec.rb +2 -0
  112. data/spec/opal/core/module_spec.rb +44 -0
  113. data/spec/opal/core/number/to_i_spec.rb +2 -0
  114. data/spec/opal/core/object_id_spec.rb +2 -0
  115. data/spec/opal/core/regexp/match_spec.rb +2 -0
  116. data/spec/opal/core/runtime/bridged_classes_spec.rb +38 -0
  117. data/spec/opal/core/runtime/constants_spec.rb +2 -0
  118. data/spec/opal/core/runtime/eval_spec.rb +2 -0
  119. data/spec/opal/core/runtime/exit_spec.rb +2 -0
  120. data/spec/opal/core/runtime/is_a_spec.rb +2 -0
  121. data/spec/opal/core/runtime/loaded_spec.rb +2 -0
  122. data/spec/opal/core/runtime/method_missing_spec.rb +2 -0
  123. data/spec/opal/core/runtime/rescue_spec.rb +2 -0
  124. data/spec/opal/core/runtime/string_spec.rb +2 -0
  125. data/spec/opal/core/runtime/truthy_spec.rb +2 -0
  126. data/spec/opal/core/runtime_spec.rb +2 -6
  127. data/spec/opal/core/string/to_sym_spec.rb +2 -0
  128. data/spec/opal/stdlib/js_spec.rb +2 -0
  129. data/spec/opal/stdlib/native/alias_native_spec.rb +2 -0
  130. data/spec/opal/stdlib/native/array_spec.rb +2 -0
  131. data/spec/opal/stdlib/native/date_spec.rb +2 -0
  132. data/spec/opal/stdlib/native/each_spec.rb +2 -0
  133. data/spec/opal/stdlib/native/element_reference_spec.rb +2 -0
  134. data/spec/opal/stdlib/native/exposure_spec.rb +2 -0
  135. data/spec/opal/stdlib/native/ext_spec.rb +2 -0
  136. data/spec/opal/stdlib/native/hash_spec.rb +30 -2
  137. data/spec/opal/stdlib/native/initialize_spec.rb +2 -0
  138. data/spec/opal/stdlib/native/method_missing_spec.rb +2 -0
  139. data/spec/opal/stdlib/native/native_alias_spec.rb +2 -0
  140. data/spec/opal/stdlib/native/native_class_spec.rb +2 -0
  141. data/spec/opal/stdlib/native/native_module_spec.rb +2 -0
  142. data/spec/opal/stdlib/native/native_reader_spec.rb +2 -0
  143. data/spec/opal/stdlib/native/native_writer_spec.rb +2 -0
  144. data/spec/opal/stdlib/native/new_spec.rb +2 -0
  145. data/spec/opal/stdlib/native/struct_spec.rb +2 -0
  146. data/spec/spec_helper.rb +2 -0
  147. data/stdlib/await.rb +1 -0
  148. data/stdlib/base64.rb +2 -0
  149. data/stdlib/bigdecimal/bignumber.js.rb +2 -0
  150. data/stdlib/bigdecimal/util.rb +1 -0
  151. data/stdlib/bigdecimal.rb +2 -0
  152. data/stdlib/buffer/array.rb +2 -0
  153. data/stdlib/buffer/view.rb +2 -0
  154. data/stdlib/buffer.rb +2 -0
  155. data/stdlib/cgi.rb +14 -0
  156. data/stdlib/console.rb +2 -0
  157. data/stdlib/date/date_time.rb +2 -0
  158. data/stdlib/date.rb +2 -0
  159. data/stdlib/delegate.rb +2 -0
  160. data/stdlib/deno/base.rb +2 -0
  161. data/stdlib/deno/file.rb +2 -0
  162. data/stdlib/erb.rb +2 -0
  163. data/stdlib/gjs/io.rb +2 -0
  164. data/stdlib/gjs/kernel.rb +2 -0
  165. data/stdlib/headless_browser/base.rb +2 -0
  166. data/stdlib/headless_browser/file.rb +1 -0
  167. data/stdlib/headless_browser.rb +1 -0
  168. data/stdlib/js.rb +2 -0
  169. data/stdlib/json.rb +9 -15
  170. data/stdlib/logger.rb +2 -0
  171. data/stdlib/nashorn/file.rb +2 -0
  172. data/stdlib/nashorn/io.rb +2 -0
  173. data/stdlib/native.rb +48 -48
  174. data/stdlib/nodejs/base.rb +2 -0
  175. data/stdlib/nodejs/dir.rb +2 -0
  176. data/stdlib/nodejs/env.rb +2 -0
  177. data/stdlib/nodejs/file.rb +2 -0
  178. data/stdlib/nodejs/fileutils.rb +2 -0
  179. data/stdlib/nodejs/io.rb +2 -0
  180. data/stdlib/nodejs/js-yaml-3-6-1.js +1 -1
  181. data/stdlib/nodejs/kernel.rb +2 -0
  182. data/stdlib/nodejs/open-uri.rb +2 -0
  183. data/stdlib/nodejs/pathname.rb +2 -0
  184. data/stdlib/nodejs/require.rb +2 -0
  185. data/stdlib/nodejs/yaml.rb +9 -3
  186. data/stdlib/opal/miniracer.rb +2 -0
  187. data/stdlib/opal-parser.rb +8 -1
  188. data/stdlib/opal-platform.rb +2 -0
  189. data/stdlib/opal-replutils.rb +2 -0
  190. data/stdlib/open-uri.rb +4 -1
  191. data/stdlib/ostruct.rb +4 -2
  192. data/stdlib/pathname.rb +2 -0
  193. data/stdlib/pp.rb +1 -0
  194. data/stdlib/promise/v2.rb +2 -0
  195. data/stdlib/quickjs/io.rb +2 -0
  196. data/stdlib/quickjs/kernel.rb +2 -0
  197. data/stdlib/quickjs.rb +2 -0
  198. data/stdlib/securerandom.rb +2 -0
  199. data/stdlib/strscan.rb +2 -0
  200. data/stdlib/time.rb +2 -0
  201. data/stdlib/uri.rb +1 -0
  202. data/tasks/performance/optimization_status.rb +2 -0
  203. data/tasks/testing.rake +1 -0
  204. data/test/nodejs/test_await.rb +1 -0
  205. data/test/nodejs/test_dir.rb +2 -0
  206. data/test/nodejs/test_error.rb +2 -0
  207. data/test/nodejs/test_file.rb +2 -0
  208. data/test/nodejs/test_string.rb +2 -0
  209. data/test/nodejs/test_yaml.rb +20 -0
  210. metadata +22 -13
  211. data/spec/filters/bugs/openstruct.rb +0 -8
@@ -538,6 +538,13 @@
538
538
  Opal.$$$ = Opal.const_get_qualified;
539
539
  Opal.$r = Opal.const_get_relative_factory;
540
540
 
541
+ function descends_from_bridged_class(klass) {
542
+ if (klass == null) return false;
543
+ if (klass.$$bridge) return klass;
544
+ if (klass.$$super) return descends_from_bridged_class(klass.$$super);
545
+ return false;
546
+ }
547
+
541
548
  // Modules & Classes
542
549
  // -----------------
543
550
 
@@ -567,14 +574,13 @@
567
574
  // @return new [Class] or existing ruby class
568
575
  //
569
576
  function $allocate_class(name, superclass, singleton) {
570
- var klass;
577
+ var klass, bridged_descendant;
571
578
 
572
- if (superclass != null && superclass.$$bridge) {
579
+ if (bridged_descendant = descends_from_bridged_class(superclass)) {
573
580
  // Inheritance from bridged classes requires
574
581
  // calling original JS constructors
575
582
  klass = function() {
576
- var args = $slice(arguments),
577
- self = new ($bind.apply(superclass.$$constructor, [null].concat(args)))();
583
+ var self = new ($bind.apply(bridged_descendant.$$constructor, $prepend(null, arguments)))();
578
584
 
579
585
  // and replacing a __proto__ manually
580
586
  $set_proto(self, klass.$$prototype);
@@ -1538,7 +1544,6 @@
1538
1544
  // @param method_name [String] The js-name of the method to stub (e.g. "$foo")
1539
1545
  // @return [undefined]
1540
1546
  Opal.stub_for = function(method_name) {
1541
-
1542
1547
  function method_missing_stub() {
1543
1548
  // Copy any given block onto the method_missing dispatcher
1544
1549
  this.$method_missing.$$p = method_missing_stub.$$p;
@@ -1547,11 +1552,8 @@
1547
1552
  method_missing_stub.$$p = null;
1548
1553
 
1549
1554
  // call method missing with correct args (remove '$' prefix on method name)
1550
- var args_ary = new Array(arguments.length);
1551
- for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = arguments[i]; }
1552
-
1553
- return this.$method_missing.apply(this, [method_name.slice(1)].concat(args_ary));
1554
- }
1555
+ return this.$method_missing.apply(this, $prepend(method_name.slice(1), arguments));
1556
+ };
1555
1557
 
1556
1558
  method_missing_stub.$$stub = true;
1557
1559
 
@@ -1693,13 +1695,6 @@
1693
1695
  }
1694
1696
  }
1695
1697
 
1696
- if (!args.$$is_array) {
1697
- var args_ary = new Array(args.length);
1698
- for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = args[i]; }
1699
-
1700
- return block.apply(null, args_ary);
1701
- }
1702
-
1703
1698
  return block.apply(null, args);
1704
1699
  };
1705
1700
 
@@ -1812,11 +1807,10 @@
1812
1807
  };
1813
1808
 
1814
1809
  // Used for extracting keyword arguments from arguments passed to
1815
- // JS function. If provided +arguments+ list doesn't have a Hash
1816
- // as a last item, returns a blank Hash.
1810
+ // JS function.
1817
1811
  //
1818
1812
  // @param parameters [Array]
1819
- // @return [Hash]
1813
+ // @return [Hash] or undefined
1820
1814
  //
1821
1815
  Opal.extract_kwargs = function(parameters) {
1822
1816
  var kwargs = parameters[parameters.length - 1];
@@ -1828,7 +1822,7 @@
1828
1822
 
1829
1823
  // Used to get a list of rest keyword arguments. Method takes the given
1830
1824
  // keyword args, i.e. the hash literal passed to the method containing all
1831
- // keyword arguemnts passed to method, as well as the used args which are
1825
+ // keyword arguments passed to method, as well as the used args which are
1832
1826
  // the names of required and optional arguments defined. This method then
1833
1827
  // just returns all key/value pairs which have not been used, in a new
1834
1828
  // hash literal.
@@ -1838,19 +1832,16 @@
1838
1832
  // @return [Hash]
1839
1833
  //
1840
1834
  Opal.kwrestargs = function(given_args, used_args) {
1841
- var keys = [],
1842
- map = {},
1843
- key ,
1844
- given_map = given_args.$$smap;
1835
+ var map = new Map();
1845
1836
 
1846
- for (key in given_map) {
1837
+ Opal.hash_each(given_args, false, function(key, value) {
1847
1838
  if (!used_args[key]) {
1848
- keys.push(key);
1849
- map[key] = given_map[key];
1839
+ Opal.hash_put(map, key, value);
1850
1840
  }
1851
- }
1841
+ return [false, false];
1842
+ });
1852
1843
 
1853
- return Opal.hash2(keys, map);
1844
+ return map;
1854
1845
  };
1855
1846
 
1856
1847
  function apply_blockopts(block, blockopts) {
@@ -1863,17 +1854,17 @@
1863
1854
  }
1864
1855
 
1865
1856
  // Optimization for a costly operation of prepending '$' to method names
1866
- var jsid_cache = new Map();
1857
+ var jsid_cache = {}
1867
1858
  function $jsid(name) {
1868
- var jsid = jsid_cache.get(name);
1869
- if (!jsid) {
1870
- jsid = '$' + name;
1871
- jsid_cache.set(name, jsid);
1872
- }
1873
- return jsid;
1859
+ return jsid_cache[name] || (jsid_cache[name] = '$' + name);
1874
1860
  }
1875
1861
  Opal.jsid = $jsid;
1876
1862
 
1863
+ function $prepend(first, second) {
1864
+ if (!second.$$is_array) second = $slice(second);
1865
+ return [first].concat(second);
1866
+ }
1867
+
1877
1868
  // Calls passed method on a ruby object with arguments and block:
1878
1869
  //
1879
1870
  // Can take a method or a method name.
@@ -1917,7 +1908,7 @@
1917
1908
  Opal.send2 = function(recv, body, method, args, block, blockopts) {
1918
1909
  if (body == null && method != null && recv.$method_missing) {
1919
1910
  body = recv.$method_missing;
1920
- args = [method].concat(args);
1911
+ args = $prepend(method, args);
1921
1912
  }
1922
1913
 
1923
1914
  apply_blockopts(block, blockopts);
@@ -2073,6 +2064,18 @@
2073
2064
  return Opal.defn(Opal.get_singleton_class(obj), jsid, body);
2074
2065
  };
2075
2066
 
2067
+ // Since JavaScript has no concept of modules, we create proxy classes
2068
+ // called `iclasses` that store copies of methods loaded. We need to
2069
+ // update them if we remove a method.
2070
+ function remove_method_from_iclasses(obj, jsid) {
2071
+ if (obj.$$is_module) {
2072
+ for (var i = 0, iclasses = obj.$$iclasses, length = iclasses.length; i < length; i++) {
2073
+ var iclass = iclasses[i];
2074
+ delete iclass[jsid];
2075
+ }
2076
+ }
2077
+ }
2078
+
2076
2079
  // Called from #remove_method.
2077
2080
  Opal.rdef = function(obj, jsid) {
2078
2081
  if (!$has_own(obj.$$prototype, jsid)) {
@@ -2081,6 +2084,8 @@
2081
2084
 
2082
2085
  delete obj.$$prototype[jsid];
2083
2086
 
2087
+ remove_method_from_iclasses(obj, jsid);
2088
+
2084
2089
  if (obj.$$is_singleton) {
2085
2090
  if (obj.$$prototype.$singleton_method_removed && !obj.$$prototype.$singleton_method_removed.$$stub) {
2086
2091
  obj.$$prototype.$singleton_method_removed(jsid.substr(1));
@@ -2101,6 +2106,8 @@
2101
2106
 
2102
2107
  Opal.add_stub_for(obj.$$prototype, jsid);
2103
2108
 
2109
+ remove_method_from_iclasses(obj, jsid);
2110
+
2104
2111
  if (obj.$$is_singleton) {
2105
2112
  if (obj.$$prototype.$singleton_method_undefined && !obj.$$prototype.$singleton_method_undefined.$$stub) {
2106
2113
  obj.$$prototype.$singleton_method_undefined(jsid.substr(1));
@@ -2160,16 +2167,11 @@
2160
2167
  // We need a wrapper because otherwise properties
2161
2168
  // would be overwritten on the original body.
2162
2169
  alias = function() {
2163
- var block = alias.$$p, args, i, ii;
2164
-
2165
- args = new Array(arguments.length);
2166
- for(i = 0, ii = arguments.length; i < ii; i++) {
2167
- args[i] = arguments[i];
2168
- }
2170
+ var block = alias.$$p, i, ii;
2169
2171
 
2170
2172
  alias.$$p = null;
2171
2173
 
2172
- return Opal.send(this, body, args, block);
2174
+ return Opal.send(this, body, arguments, block);
2173
2175
  };
2174
2176
 
2175
2177
  // Assign the 'length' value with defineProperty because
@@ -2225,228 +2227,169 @@
2225
2227
  // Hashes
2226
2228
  // ------
2227
2229
 
2228
- Opal.hash_init = function(hash) {
2229
- hash.$$smap = Object.create(null);
2230
- hash.$$map = Object.create(null);
2231
- hash.$$keys = [];
2232
- };
2230
+ Opal.hash_init = function (_hash) {
2231
+ console.warn("DEPRECATION: Opal.hash_init is deprecated and is now a no-op.");
2232
+ }
2233
2233
 
2234
2234
  Opal.hash_clone = function(from_hash, to_hash) {
2235
2235
  to_hash.$$none = from_hash.$$none;
2236
2236
  to_hash.$$proc = from_hash.$$proc;
2237
2237
 
2238
- for (var i = 0, keys = from_hash.$$keys, smap = from_hash.$$smap, len = keys.length, key, value; i < len; i++) {
2239
- key = keys[i];
2240
-
2241
- if (key.$$is_string) {
2242
- value = smap[key];
2243
- } else {
2244
- value = key.value;
2245
- key = key.key;
2246
- }
2247
-
2238
+ return Opal.hash_each(from_hash, to_hash, function(key, value) {
2248
2239
  Opal.hash_put(to_hash, key, value);
2249
- }
2240
+ return [false, to_hash];
2241
+ });
2250
2242
  };
2251
2243
 
2252
2244
  Opal.hash_put = function(hash, key, value) {
2253
- if (key.$$is_string) {
2254
- if (!$has_own(hash.$$smap, key)) {
2255
- hash.$$keys.push(key);
2256
- }
2257
- hash.$$smap[key] = value;
2258
- return;
2259
- }
2245
+ var type = typeof key;
2246
+ if (type === "string" || type === "symbol" || type === "number" || type === "boolean" || type === "bigint") {
2247
+ hash.set(key, value)
2248
+ } else if (key.$$is_string) {
2249
+ hash.set(key.valueOf(), value);
2250
+ } else {
2251
+ if (!hash.$$keys)
2252
+ hash.$$keys = new Map();
2260
2253
 
2261
- var key_hash, bucket, last_bucket;
2262
- key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash();
2254
+ var key_hash = key.$$is_string ? key.valueOf() : (hash.$$by_identity ? Opal.id(key) : key.$hash()),
2255
+ keys = hash.$$keys;
2263
2256
 
2264
- if (!$has_own(hash.$$map, key_hash)) {
2265
- bucket = {key: key, key_hash: key_hash, value: value};
2266
- hash.$$keys.push(bucket);
2267
- hash.$$map[key_hash] = bucket;
2268
- return;
2269
- }
2257
+ if (!keys.has(key_hash)) {
2258
+ keys.set(key_hash, [key]);
2259
+ hash.set(key, value);
2260
+ return;
2261
+ }
2270
2262
 
2271
- bucket = hash.$$map[key_hash];
2263
+ var objects = keys.get(key_hash),
2264
+ object;
2272
2265
 
2273
- while (bucket) {
2274
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2275
- last_bucket = undefined;
2276
- bucket.value = value;
2277
- break;
2266
+ for (var i=0; i<objects.length; i++) {
2267
+ object = objects[0];
2268
+ if (key === object || key['$eql?'](object)) {
2269
+ hash.set(object, value);
2270
+ return;
2271
+ }
2278
2272
  }
2279
- last_bucket = bucket;
2280
- bucket = bucket.next;
2281
- }
2282
2273
 
2283
- if (last_bucket) {
2284
- bucket = {key: key, key_hash: key_hash, value: value};
2285
- hash.$$keys.push(bucket);
2286
- last_bucket.next = bucket;
2274
+ objects.push(key);
2275
+ hash.set(key, value);
2287
2276
  }
2288
2277
  };
2289
2278
 
2290
2279
  Opal.hash_get = function(hash, key) {
2291
- if (key.$$is_string) {
2292
- if ($has_own(hash.$$smap, key)) {
2293
- return hash.$$smap[key];
2294
- }
2295
- return;
2296
- }
2297
-
2298
- var key_hash, bucket;
2299
- key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash();
2300
-
2301
- if ($has_own(hash.$$map, key_hash)) {
2302
- bucket = hash.$$map[key_hash];
2303
-
2304
- while (bucket) {
2305
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2306
- return bucket.value;
2280
+ var type = typeof key;
2281
+ if (type === "string" || type === "symbol" || type === "number" || type === "boolean" || type === "bigint") {
2282
+ return hash.get(key)
2283
+ } else if (hash.$$keys) {
2284
+ var key_hash = key.$$is_string ? key.valueOf() : (hash.$$by_identity ? Opal.id(key) : key.$hash()),
2285
+ objects = hash.$$keys.get(key_hash),
2286
+ object;
2287
+
2288
+ if (objects !== undefined) {
2289
+ for (var i=0; i<objects.length; i++) {
2290
+ object = objects[i];
2291
+ if (key === object || key['$eql?'](object))
2292
+ return hash.get(object);
2307
2293
  }
2308
- bucket = bucket.next;
2294
+ } else if (key.$$is_string) {
2295
+ return hash.get(key_hash);
2309
2296
  }
2297
+ } else if (key.$$is_string) {
2298
+ return hash.get(key.valueOf());
2310
2299
  }
2311
2300
  };
2312
2301
 
2313
- Opal.hash_delete = function(hash, key) {
2314
- var i, keys = hash.$$keys, length = keys.length, value, key_tmp;
2315
-
2316
- if (key.$$is_string) {
2317
- if (typeof key !== "string") key = key.valueOf();
2318
-
2319
- if (!$has_own(hash.$$smap, key)) {
2320
- return;
2321
- }
2322
-
2323
- for (i = 0; i < length; i++) {
2324
- key_tmp = keys[i];
2325
-
2326
- if (key_tmp.$$is_string && typeof key_tmp !== "string") {
2327
- key_tmp = key_tmp.valueOf();
2328
- }
2329
-
2330
- if (key_tmp === key) {
2331
- keys.splice(i, 1);
2332
- break;
2333
- }
2334
- }
2335
-
2336
- value = hash.$$smap[key];
2337
- delete hash.$$smap[key];
2302
+ function $hash_delete_stage2(hash, key) {
2303
+ var value = hash.get(key);
2304
+ if (value !== undefined) {
2305
+ hash.delete(key);
2338
2306
  return value;
2339
2307
  }
2308
+ }
2340
2309
 
2341
- var key_hash = key.$hash();
2342
-
2343
- if (!$has_own(hash.$$map, key_hash)) {
2344
- return;
2345
- }
2346
-
2347
- var bucket = hash.$$map[key_hash], last_bucket;
2348
-
2349
- while (bucket) {
2350
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2351
- value = bucket.value;
2352
-
2353
- for (i = 0; i < length; i++) {
2354
- if (keys[i] === bucket) {
2355
- keys.splice(i, 1);
2356
- break;
2310
+ Opal.hash_delete = function(hash, key) {
2311
+ var type = typeof key
2312
+ if (type === "string" || type === "symbol" || type === "number" || type === "boolean" || type === "bigint") {
2313
+ return $hash_delete_stage2(hash, key);
2314
+ } else if (hash.$$keys) {
2315
+ var key_hash = key.$$is_string ? key.valueOf() : (hash.$$by_identity ? Opal.id(key) : key.$hash()),
2316
+ objects = hash.$$keys.get(key_hash),
2317
+ object;
2318
+
2319
+ if (objects !== undefined) {
2320
+ for (var i=0; i<objects.length; i++) {
2321
+ object = objects[i];
2322
+ if (key === object || key['$eql?'](object)) {
2323
+ objects.splice(i, 1);
2324
+ if (objects.length === 0)
2325
+ hash.$$keys.delete(key_hash);
2326
+ return $hash_delete_stage2(hash, object);
2357
2327
  }
2358
2328
  }
2359
-
2360
- if (last_bucket && bucket.next) {
2361
- last_bucket.next = bucket.next;
2362
- }
2363
- else if (last_bucket) {
2364
- delete last_bucket.next;
2365
- }
2366
- else if (bucket.next) {
2367
- hash.$$map[key_hash] = bucket.next;
2368
- }
2369
- else {
2370
- delete hash.$$map[key_hash];
2371
- }
2372
-
2373
- return value;
2329
+ } else if (key.$$is_string) {
2330
+ return $hash_delete_stage2(hash, key_hash);
2374
2331
  }
2375
- last_bucket = bucket;
2376
- bucket = bucket.next;
2332
+ } else if (key.$$is_string) {
2333
+ return $hash_delete_stage2(hash, key.valueOf());
2377
2334
  }
2378
2335
  };
2379
2336
 
2380
2337
  Opal.hash_rehash = function(hash) {
2381
- for (var i = 0, length = hash.$$keys.length, key_hash, bucket, last_bucket; i < length; i++) {
2338
+ var keys = hash.$$keys;
2382
2339
 
2383
- if (hash.$$keys[i].$$is_string) {
2384
- continue;
2385
- }
2340
+ if (keys)
2341
+ keys.clear();
2386
2342
 
2387
- key_hash = hash.$$keys[i].key.$hash();
2343
+ Opal.hash_each(hash, false, function(key, value) {
2344
+ var type = typeof key;
2345
+ if (type === "string" || type === "symbol" || type === "number" || type === "boolean" || type === "bigint")
2346
+ return [false, false]; // nothing to rehash
2388
2347
 
2389
- if (key_hash === hash.$$keys[i].key_hash) {
2390
- continue;
2391
- }
2348
+ var key_hash = key.$$is_string ? key.valueOf() : (hash.$$by_identity ? Opal.id(key) : key.$hash());
2392
2349
 
2393
- bucket = hash.$$map[hash.$$keys[i].key_hash];
2394
- last_bucket = undefined;
2350
+ if (!keys)
2351
+ hash.$$keys = keys = new Map();
2395
2352
 
2396
- while (bucket) {
2397
- if (bucket === hash.$$keys[i]) {
2398
- if (last_bucket && bucket.next) {
2399
- last_bucket.next = bucket.next;
2400
- }
2401
- else if (last_bucket) {
2402
- delete last_bucket.next;
2403
- }
2404
- else if (bucket.next) {
2405
- hash.$$map[hash.$$keys[i].key_hash] = bucket.next;
2406
- }
2407
- else {
2408
- delete hash.$$map[hash.$$keys[i].key_hash];
2409
- }
2410
- break;
2411
- }
2412
- last_bucket = bucket;
2413
- bucket = bucket.next;
2353
+ if (!keys.has(key_hash)) {
2354
+ keys.set(key_hash, [key]);
2355
+ return [false, false];
2414
2356
  }
2415
2357
 
2416
- hash.$$keys[i].key_hash = key_hash;
2358
+ var objects = keys.get(key_hash),
2359
+ objects_copy = $slice(objects),
2360
+ object;
2417
2361
 
2418
- if (!$has_own(hash.$$map, key_hash)) {
2419
- hash.$$map[key_hash] = hash.$$keys[i];
2420
- continue;
2362
+ for (var i=0; i<objects_copy.length; i++) {
2363
+ object = objects_copy[i];
2364
+ if (key === object || key['$eql?'](object)) {
2365
+ // got a duplicate, remove it
2366
+ objects.splice(objects.indexOf(object), 1);
2367
+ hash.delete(object);
2368
+ }
2421
2369
  }
2422
2370
 
2423
- bucket = hash.$$map[key_hash];
2424
- last_bucket = undefined;
2371
+ objects.push(key);
2425
2372
 
2426
- while (bucket) {
2427
- if (bucket === hash.$$keys[i]) {
2428
- last_bucket = undefined;
2429
- break;
2430
- }
2431
- last_bucket = bucket;
2432
- bucket = bucket.next;
2433
- }
2373
+ return [false, false]
2374
+ });
2434
2375
 
2435
- if (last_bucket) {
2436
- last_bucket.next = hash.$$keys[i];
2437
- }
2438
- }
2376
+ return hash;
2439
2377
  };
2440
2378
 
2441
- Opal.hash = function() {
2442
- var arguments_length = arguments.length, args, hash, i, length, key, value;
2379
+ Opal.hash = function () {
2380
+ var arguments_length = arguments.length,
2381
+ args,
2382
+ hash,
2383
+ i,
2384
+ length,
2385
+ key,
2386
+ value;
2443
2387
 
2444
2388
  if (arguments_length === 1 && arguments[0].$$is_hash) {
2445
2389
  return arguments[0];
2446
2390
  }
2447
2391
 
2448
- hash = new Opal.Hash();
2449
- Opal.hash_init(hash);
2392
+ hash = new Map();
2450
2393
 
2451
2394
  if (arguments_length === 1) {
2452
2395
  args = arguments[0];
@@ -2456,7 +2399,7 @@
2456
2399
 
2457
2400
  for (i = 0; i < length; i++) {
2458
2401
  if (args[i].length !== 2) {
2459
- $raise(Opal.ArgumentError, "value not of length 2: " + args[i].$inspect());
2402
+ $raise(Opal.ArgumentError, 'value not of length 2: ' + args[i].$inspect());
2460
2403
  }
2461
2404
 
2462
2405
  key = args[i][0];
@@ -2466,8 +2409,7 @@
2466
2409
  }
2467
2410
 
2468
2411
  return hash;
2469
- }
2470
- else {
2412
+ } else {
2471
2413
  args = arguments[0];
2472
2414
  for (key in args) {
2473
2415
  if ($has_own(args, key)) {
@@ -2482,7 +2424,7 @@
2482
2424
  }
2483
2425
 
2484
2426
  if (arguments_length % 2 !== 0) {
2485
- $raise(Opal.ArgumentError, "odd number of arguments for Hash");
2427
+ $raise(Opal.ArgumentError, 'odd number of arguments for Hash');
2486
2428
  }
2487
2429
 
2488
2430
  for (i = 0; i < arguments_length; i += 2) {
@@ -2501,15 +2443,29 @@
2501
2443
  // function.
2502
2444
  //
2503
2445
  Opal.hash2 = function(keys, smap) {
2504
- var hash = new Opal.Hash();
2505
-
2506
- hash.$$smap = smap;
2507
- hash.$$map = Object.create(null);
2508
- hash.$$keys = keys;
2446
+ console.warn("DEPRECATION: `Opal.hash2` is deprecated and will be removed in Opal 2.0. Use `new Map()` with an array of key/value pairs instead.");
2509
2447
 
2448
+ var hash = new Map();
2449
+ for (var i = 0, max = keys.length; i < max; i++) {
2450
+ hash.set(keys[i], smap[keys[i]]);
2451
+ }
2510
2452
  return hash;
2511
2453
  };
2512
2454
 
2455
+ Opal.hash_each = function (hash, dres, fun) {
2456
+ // dres = default result, returned if hash is empty
2457
+ // fun is called as fun(key, value) and must return a array with [break, result]
2458
+ // if break is true, iteration stops and result is returned
2459
+ // if break is false, iteration continues and eventually the last result is returned
2460
+ var res;
2461
+ for (var i = 0, entry, entries = Array.from(hash.entries()), l = entries.length; i < l; i++) {
2462
+ entry = entries[i];
2463
+ res = fun(entry[0], entry[1]);
2464
+ if (res[0]) return res[1];
2465
+ }
2466
+ return res ? res[1] : dres;
2467
+ };
2468
+
2513
2469
  // Create a new range instance with first and last values, and whether the
2514
2470
  // range excludes the last value.
2515
2471
  //
@@ -2547,7 +2503,7 @@
2547
2503
  // helper that can be used from methods
2548
2504
  function $deny_frozen_access(obj) {
2549
2505
  if (obj.$$frozen) {
2550
- $raise(Opal.FrozenError, "can't modify frozen " + (obj.$class()) + ": " + (obj), Opal.hash2(["receiver"], {"receiver": obj}));
2506
+ $raise(Opal.FrozenError, "can't modify frozen " + (obj.$class()) + ": " + (obj), new Map([["receiver", obj]]));
2551
2507
  }
2552
2508
  };
2553
2509
  Opal.deny_frozen_access = $deny_frozen_access;
@@ -2672,7 +2628,7 @@
2672
2628
  if (part.ignoreCase !== ignoreCase)
2673
2629
  Opal.Kernel.$warn(
2674
2630
  "ignore case doesn't match for " + part.source.$inspect(),
2675
- Opal.hash({uplevel: 1})
2631
+ new Map([['uplevel', 1]])
2676
2632
  )
2677
2633
 
2678
2634
  part = part.source;
@@ -2923,7 +2879,7 @@
2923
2879
  // Primitives for handling parameters
2924
2880
  Opal.ensure_kwargs = function(kwargs) {
2925
2881
  if (kwargs == null) {
2926
- return Opal.hash2([], {});
2882
+ return new Map();
2927
2883
  } else if (kwargs.$$is_hash) {
2928
2884
  return kwargs;
2929
2885
  } else {
@@ -2932,10 +2888,11 @@
2932
2888
  }
2933
2889
 
2934
2890
  Opal.get_kwarg = function(kwargs, key) {
2935
- if (!$has_own(kwargs.$$smap, key)) {
2891
+ var kwarg = Opal.hash_get(kwargs, key);
2892
+ if (kwarg === undefined) {
2936
2893
  $raise(Opal.ArgumentError, 'missing keyword: '+key);
2937
2894
  }
2938
- return kwargs.$$smap[key];
2895
+ return kwarg;
2939
2896
  }
2940
2897
 
2941
2898
  // Arrays of size > 32 elements that contain only strings,
@@ -3020,10 +2977,9 @@
3020
2977
 
3021
2978
  // Foward calls to define_method on the top object to Object
3022
2979
  function top_define_method() {
3023
- var args = $slice(arguments);
3024
2980
  var block = top_define_method.$$p;
3025
2981
  top_define_method.$$p = null;
3026
- return Opal.send(_Object, 'define_method', args, block)
2982
+ return Opal.send(_Object, 'define_method', arguments, block)
3027
2983
  };
3028
2984
 
3029
2985
  // Nil
@@ -3037,7 +2993,7 @@
3037
2993
  Object.seal(nil);
3038
2994
 
3039
2995
  Opal.thrower = function(type) {
3040
- var thrower = new Error('unexpected '+type);
2996
+ var thrower = { message: 'unexpected '+type };
3041
2997
  thrower.$thrower_type = type;
3042
2998
  thrower.$throw = function(value) {
3043
2999
  if (value == null) value = nil;
data/opal/corelib/set.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  # helpers: freeze
2
+ # backtick_javascript: true
3
+
2
4
  # Portions Copyright (c) 2002-2013 Akinori MUSHA <knu@iDaemons.org>
3
5
  class ::Set
4
6
  include ::Enumerable
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  require 'corelib/string'
2
4
 
3
5
  class ::Encoding
@@ -103,6 +105,7 @@ class ::Encoding
103
105
 
104
106
  class ::EncodingError < ::StandardError; end
105
107
  class ::CompatibilityError < ::EncodingError; end
108
+ class UndefinedConversionError < ::EncodingError; end
106
109
  end
107
110
 
108
111
  ::Encoding.register 'UTF-8', aliases: ['CP65001'], ascii: true do