opal 1.7.4 → 1.8.0.alpha1

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