opal 1.7.3 → 1.8.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -0
  3. data/.github/workflows/build.yml +9 -9
  4. data/.rubocop/todo.yml +2 -2
  5. data/.rubocop.yml +17 -10
  6. data/.rubocop_todo.yml +311 -0
  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_cdp_interface.rb +1 -0
  14. data/lib/opal/cli_runners/firefox_cdp_interface.rb +1 -0
  15. data/lib/opal/compiler.rb +33 -1
  16. data/lib/opal/nodes/args/extract_kwoptarg.rb +2 -1
  17. data/lib/opal/nodes/call.rb +1 -1
  18. data/lib/opal/nodes/call_special.rb +71 -47
  19. data/lib/opal/nodes/hash.rb +14 -30
  20. data/lib/opal/nodes/if.rb +38 -30
  21. data/lib/opal/nodes/literal.rb +5 -1
  22. data/lib/opal/nodes/x_string.rb +13 -0
  23. data/lib/opal/parser/patch.rb +1 -0
  24. data/lib/opal/rewriters/for_rewriter.rb +36 -24
  25. data/lib/opal/source_map/file.rb +1 -1
  26. data/lib/opal/version.rb +1 -1
  27. data/opal/corelib/array/pack.rb +1 -0
  28. data/opal/corelib/array.rb +71 -43
  29. data/opal/corelib/basic_object.rb +1 -0
  30. data/opal/corelib/binding.rb +2 -0
  31. data/opal/corelib/boolean.rb +1 -0
  32. data/opal/corelib/class.rb +2 -0
  33. data/opal/corelib/comparable.rb +1 -0
  34. data/opal/corelib/complex.rb +2 -0
  35. data/opal/corelib/constants.rb +2 -2
  36. data/opal/corelib/dir.rb +2 -0
  37. data/opal/corelib/enumerable.rb +3 -2
  38. data/opal/corelib/enumerator/arithmetic_sequence.rb +2 -0
  39. data/opal/corelib/enumerator/chain.rb +1 -0
  40. data/opal/corelib/enumerator/generator.rb +1 -0
  41. data/opal/corelib/enumerator/lazy.rb +1 -0
  42. data/opal/corelib/enumerator/yielder.rb +2 -0
  43. data/opal/corelib/enumerator.rb +1 -0
  44. data/opal/corelib/error/errno.rb +2 -0
  45. data/opal/corelib/error.rb +12 -0
  46. data/opal/corelib/file.rb +1 -0
  47. data/opal/corelib/hash.rb +197 -504
  48. data/opal/corelib/helpers.rb +1 -0
  49. data/opal/corelib/io.rb +2 -0
  50. data/opal/corelib/irb.rb +2 -0
  51. data/opal/corelib/kernel/format.rb +1 -0
  52. data/opal/corelib/kernel.rb +70 -14
  53. data/opal/corelib/main.rb +2 -0
  54. data/opal/corelib/marshal/read_buffer.rb +2 -0
  55. data/opal/corelib/marshal/write_buffer.rb +2 -0
  56. data/opal/corelib/math/polyfills.rb +2 -0
  57. data/opal/corelib/math.rb +1 -0
  58. data/opal/corelib/method.rb +2 -0
  59. data/opal/corelib/module.rb +1 -0
  60. data/opal/corelib/nil.rb +3 -1
  61. data/opal/corelib/number.rb +2 -0
  62. data/opal/corelib/numeric.rb +2 -0
  63. data/opal/corelib/object_space.rb +1 -0
  64. data/opal/corelib/pack_unpack/format_string_parser.rb +2 -0
  65. data/opal/corelib/proc.rb +30 -28
  66. data/opal/corelib/process.rb +2 -0
  67. data/opal/corelib/random/formatter.rb +2 -0
  68. data/opal/corelib/random/math_random.js.rb +2 -0
  69. data/opal/corelib/random/mersenne_twister.rb +2 -0
  70. data/opal/corelib/random/seedrandom.js.rb +2 -0
  71. data/opal/corelib/random.rb +1 -0
  72. data/opal/corelib/range.rb +34 -12
  73. data/opal/corelib/rational.rb +2 -0
  74. data/opal/corelib/regexp.rb +5 -1
  75. data/opal/corelib/runtime.js +185 -224
  76. data/opal/corelib/set.rb +2 -0
  77. data/opal/corelib/string/encoding.rb +3 -0
  78. data/opal/corelib/string/unpack.rb +2 -0
  79. data/opal/corelib/string.rb +20 -12
  80. data/opal/corelib/struct.rb +3 -1
  81. data/opal/corelib/time.rb +1 -0
  82. data/opal/corelib/trace_point.rb +2 -0
  83. data/opal/corelib/unsupported.rb +2 -0
  84. data/opal/corelib/variables.rb +2 -0
  85. data/opal.gemspec +2 -2
  86. data/spec/filters/bugs/array.rb +0 -2
  87. data/spec/filters/bugs/enumerable.rb +0 -3
  88. data/spec/filters/bugs/hash.rb +0 -13
  89. data/spec/filters/bugs/kernel.rb +0 -38
  90. data/spec/filters/bugs/range.rb +0 -1
  91. data/spec/filters/bugs/ruby-32.rb +0 -2
  92. data/spec/filters/bugs/string.rb +0 -1
  93. data/spec/filters/bugs/struct.rb +1 -5
  94. data/spec/filters/unsupported/hash.rb +1 -0
  95. data/spec/lib/compiler_spec.rb +24 -17
  96. data/spec/mspec-opal/formatters.rb +2 -0
  97. data/spec/mspec-opal/runner.rb +2 -0
  98. data/spec/opal/core/array/dup_spec.rb +2 -0
  99. data/spec/opal/core/exception_spec.rb +2 -0
  100. data/spec/opal/core/hash/internals_spec.rb +154 -206
  101. data/spec/opal/core/hash_spec.rb +2 -0
  102. data/spec/opal/core/iterable_props_spec.rb +2 -0
  103. data/spec/opal/core/kernel/at_exit_spec.rb +2 -0
  104. data/spec/opal/core/kernel/respond_to_spec.rb +2 -0
  105. data/spec/opal/core/language/arguments/mlhs_arg_spec.rb +2 -0
  106. data/spec/opal/core/language/case_spec.rb +13 -0
  107. data/spec/opal/core/language/safe_navigator_spec.rb +2 -0
  108. data/spec/opal/core/language/xstring_send_spec.rb +15 -0
  109. data/spec/opal/core/language/xstring_spec.rb +2 -0
  110. data/spec/opal/core/language_spec.rb +2 -0
  111. data/spec/opal/core/module_spec.rb +44 -0
  112. data/spec/opal/core/number/to_i_spec.rb +2 -0
  113. data/spec/opal/core/object_id_spec.rb +2 -0
  114. data/spec/opal/core/regexp/match_spec.rb +2 -0
  115. data/spec/opal/core/runtime/bridged_classes_spec.rb +38 -0
  116. data/spec/opal/core/runtime/constants_spec.rb +2 -0
  117. data/spec/opal/core/runtime/eval_spec.rb +2 -0
  118. data/spec/opal/core/runtime/exit_spec.rb +2 -0
  119. data/spec/opal/core/runtime/is_a_spec.rb +2 -0
  120. data/spec/opal/core/runtime/loaded_spec.rb +2 -0
  121. data/spec/opal/core/runtime/method_missing_spec.rb +2 -0
  122. data/spec/opal/core/runtime/rescue_spec.rb +2 -0
  123. data/spec/opal/core/runtime/string_spec.rb +2 -0
  124. data/spec/opal/core/runtime/truthy_spec.rb +2 -0
  125. data/spec/opal/core/runtime_spec.rb +2 -6
  126. data/spec/opal/core/string/to_sym_spec.rb +2 -0
  127. data/spec/opal/stdlib/js_spec.rb +2 -0
  128. data/spec/opal/stdlib/native/alias_native_spec.rb +2 -0
  129. data/spec/opal/stdlib/native/array_spec.rb +2 -0
  130. data/spec/opal/stdlib/native/date_spec.rb +2 -0
  131. data/spec/opal/stdlib/native/each_spec.rb +2 -0
  132. data/spec/opal/stdlib/native/element_reference_spec.rb +2 -0
  133. data/spec/opal/stdlib/native/exposure_spec.rb +2 -0
  134. data/spec/opal/stdlib/native/ext_spec.rb +2 -0
  135. data/spec/opal/stdlib/native/hash_spec.rb +30 -2
  136. data/spec/opal/stdlib/native/initialize_spec.rb +2 -0
  137. data/spec/opal/stdlib/native/method_missing_spec.rb +2 -0
  138. data/spec/opal/stdlib/native/native_alias_spec.rb +2 -0
  139. data/spec/opal/stdlib/native/native_class_spec.rb +2 -0
  140. data/spec/opal/stdlib/native/native_module_spec.rb +2 -0
  141. data/spec/opal/stdlib/native/native_reader_spec.rb +2 -0
  142. data/spec/opal/stdlib/native/native_writer_spec.rb +2 -0
  143. data/spec/opal/stdlib/native/new_spec.rb +2 -0
  144. data/spec/opal/stdlib/native/struct_spec.rb +2 -0
  145. data/spec/spec_helper.rb +2 -0
  146. data/stdlib/await.rb +1 -0
  147. data/stdlib/base64.rb +2 -0
  148. data/stdlib/bigdecimal/bignumber.js.rb +2 -0
  149. data/stdlib/bigdecimal/util.rb +1 -0
  150. data/stdlib/bigdecimal.rb +2 -0
  151. data/stdlib/buffer/array.rb +2 -0
  152. data/stdlib/buffer/view.rb +2 -0
  153. data/stdlib/buffer.rb +2 -0
  154. data/stdlib/cgi.rb +14 -0
  155. data/stdlib/console.rb +2 -0
  156. data/stdlib/date/date_time.rb +2 -0
  157. data/stdlib/date.rb +2 -0
  158. data/stdlib/delegate.rb +2 -0
  159. data/stdlib/deno/base.rb +2 -0
  160. data/stdlib/deno/file.rb +2 -0
  161. data/stdlib/erb.rb +2 -0
  162. data/stdlib/gjs/io.rb +2 -0
  163. data/stdlib/gjs/kernel.rb +2 -0
  164. data/stdlib/headless_browser/base.rb +2 -0
  165. data/stdlib/headless_browser/file.rb +1 -0
  166. data/stdlib/headless_browser.rb +1 -0
  167. data/stdlib/js.rb +2 -0
  168. data/stdlib/json.rb +9 -15
  169. data/stdlib/logger.rb +2 -0
  170. data/stdlib/nashorn/file.rb +2 -0
  171. data/stdlib/nashorn/io.rb +2 -0
  172. data/stdlib/native.rb +48 -48
  173. data/stdlib/nodejs/base.rb +2 -0
  174. data/stdlib/nodejs/dir.rb +2 -0
  175. data/stdlib/nodejs/env.rb +2 -0
  176. data/stdlib/nodejs/file.rb +2 -0
  177. data/stdlib/nodejs/fileutils.rb +2 -0
  178. data/stdlib/nodejs/io.rb +2 -0
  179. data/stdlib/nodejs/js-yaml-3-6-1.js +1 -1
  180. data/stdlib/nodejs/kernel.rb +2 -0
  181. data/stdlib/nodejs/open-uri.rb +2 -0
  182. data/stdlib/nodejs/pathname.rb +2 -0
  183. data/stdlib/nodejs/require.rb +2 -0
  184. data/stdlib/nodejs/yaml.rb +9 -3
  185. data/stdlib/opal/miniracer.rb +2 -0
  186. data/stdlib/opal-parser.rb +8 -1
  187. data/stdlib/opal-platform.rb +2 -0
  188. data/stdlib/opal-replutils.rb +2 -0
  189. data/stdlib/open-uri.rb +4 -1
  190. data/stdlib/ostruct.rb +4 -2
  191. data/stdlib/pathname.rb +2 -0
  192. data/stdlib/pp.rb +1 -0
  193. data/stdlib/promise/v2.rb +2 -0
  194. data/stdlib/quickjs/io.rb +2 -0
  195. data/stdlib/quickjs/kernel.rb +2 -0
  196. data/stdlib/quickjs.rb +2 -0
  197. data/stdlib/securerandom.rb +2 -0
  198. data/stdlib/strscan.rb +2 -0
  199. data/stdlib/time.rb +2 -0
  200. data/stdlib/uri.rb +1 -0
  201. data/tasks/performance/optimization_status.rb +2 -0
  202. data/tasks/testing.rake +1 -0
  203. data/test/nodejs/test_await.rb +1 -0
  204. data/test/nodejs/test_dir.rb +2 -0
  205. data/test/nodejs/test_error.rb +2 -0
  206. data/test/nodejs/test_file.rb +2 -0
  207. data/test/nodejs/test_string.rb +2 -0
  208. data/test/nodejs/test_yaml.rb +20 -0
  209. metadata +23 -13
  210. 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) {
@@ -1869,6 +1860,11 @@
1869
1860
  }
1870
1861
  Opal.jsid = $jsid;
1871
1862
 
1863
+ function $prepend(first, second) {
1864
+ if (!second.$$is_array) second = $slice(second);
1865
+ return [first].concat(second);
1866
+ }
1867
+
1872
1868
  // Calls passed method on a ruby object with arguments and block:
1873
1869
  //
1874
1870
  // Can take a method or a method name.
@@ -1912,7 +1908,7 @@
1912
1908
  Opal.send2 = function(recv, body, method, args, block, blockopts) {
1913
1909
  if (body == null && method != null && recv.$method_missing) {
1914
1910
  body = recv.$method_missing;
1915
- args = [method].concat(args);
1911
+ args = $prepend(method, args);
1916
1912
  }
1917
1913
 
1918
1914
  apply_blockopts(block, blockopts);
@@ -2068,6 +2064,18 @@
2068
2064
  return Opal.defn(Opal.get_singleton_class(obj), jsid, body);
2069
2065
  };
2070
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
+
2071
2079
  // Called from #remove_method.
2072
2080
  Opal.rdef = function(obj, jsid) {
2073
2081
  if (!$has_own(obj.$$prototype, jsid)) {
@@ -2076,6 +2084,8 @@
2076
2084
 
2077
2085
  delete obj.$$prototype[jsid];
2078
2086
 
2087
+ remove_method_from_iclasses(obj, jsid);
2088
+
2079
2089
  if (obj.$$is_singleton) {
2080
2090
  if (obj.$$prototype.$singleton_method_removed && !obj.$$prototype.$singleton_method_removed.$$stub) {
2081
2091
  obj.$$prototype.$singleton_method_removed(jsid.substr(1));
@@ -2096,6 +2106,8 @@
2096
2106
 
2097
2107
  Opal.add_stub_for(obj.$$prototype, jsid);
2098
2108
 
2109
+ remove_method_from_iclasses(obj, jsid);
2110
+
2099
2111
  if (obj.$$is_singleton) {
2100
2112
  if (obj.$$prototype.$singleton_method_undefined && !obj.$$prototype.$singleton_method_undefined.$$stub) {
2101
2113
  obj.$$prototype.$singleton_method_undefined(jsid.substr(1));
@@ -2155,16 +2167,11 @@
2155
2167
  // We need a wrapper because otherwise properties
2156
2168
  // would be overwritten on the original body.
2157
2169
  alias = function() {
2158
- var block = alias.$$p, args, i, ii;
2159
-
2160
- args = new Array(arguments.length);
2161
- for(i = 0, ii = arguments.length; i < ii; i++) {
2162
- args[i] = arguments[i];
2163
- }
2170
+ var block = alias.$$p, i, ii;
2164
2171
 
2165
2172
  alias.$$p = null;
2166
2173
 
2167
- return Opal.send(this, body, args, block);
2174
+ return Opal.send(this, body, arguments, block);
2168
2175
  };
2169
2176
 
2170
2177
  // Assign the 'length' value with defineProperty because
@@ -2220,228 +2227,169 @@
2220
2227
  // Hashes
2221
2228
  // ------
2222
2229
 
2223
- Opal.hash_init = function(hash) {
2224
- hash.$$smap = Object.create(null);
2225
- hash.$$map = Object.create(null);
2226
- hash.$$keys = [];
2227
- };
2230
+ Opal.hash_init = function (_hash) {
2231
+ console.warn("DEPRECATION: Opal.hash_init is deprecated and is now a no-op.");
2232
+ }
2228
2233
 
2229
2234
  Opal.hash_clone = function(from_hash, to_hash) {
2230
2235
  to_hash.$$none = from_hash.$$none;
2231
2236
  to_hash.$$proc = from_hash.$$proc;
2232
2237
 
2233
- for (var i = 0, keys = from_hash.$$keys, smap = from_hash.$$smap, len = keys.length, key, value; i < len; i++) {
2234
- key = keys[i];
2235
-
2236
- if (key.$$is_string) {
2237
- value = smap[key];
2238
- } else {
2239
- value = key.value;
2240
- key = key.key;
2241
- }
2242
-
2238
+ return Opal.hash_each(from_hash, to_hash, function(key, value) {
2243
2239
  Opal.hash_put(to_hash, key, value);
2244
- }
2240
+ return [false, to_hash];
2241
+ });
2245
2242
  };
2246
2243
 
2247
2244
  Opal.hash_put = function(hash, key, value) {
2248
- if (key.$$is_string) {
2249
- if (!$has_own(hash.$$smap, key)) {
2250
- hash.$$keys.push(key);
2251
- }
2252
- hash.$$smap[key] = value;
2253
- return;
2254
- }
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();
2255
2253
 
2256
- var key_hash, bucket, last_bucket;
2257
- 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;
2258
2256
 
2259
- if (!$has_own(hash.$$map, key_hash)) {
2260
- bucket = {key: key, key_hash: key_hash, value: value};
2261
- hash.$$keys.push(bucket);
2262
- hash.$$map[key_hash] = bucket;
2263
- return;
2264
- }
2257
+ if (!keys.has(key_hash)) {
2258
+ keys.set(key_hash, [key]);
2259
+ hash.set(key, value);
2260
+ return;
2261
+ }
2265
2262
 
2266
- bucket = hash.$$map[key_hash];
2263
+ var objects = keys.get(key_hash),
2264
+ object;
2267
2265
 
2268
- while (bucket) {
2269
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2270
- last_bucket = undefined;
2271
- bucket.value = value;
2272
- 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
+ }
2273
2272
  }
2274
- last_bucket = bucket;
2275
- bucket = bucket.next;
2276
- }
2277
2273
 
2278
- if (last_bucket) {
2279
- bucket = {key: key, key_hash: key_hash, value: value};
2280
- hash.$$keys.push(bucket);
2281
- last_bucket.next = bucket;
2274
+ objects.push(key);
2275
+ hash.set(key, value);
2282
2276
  }
2283
2277
  };
2284
2278
 
2285
2279
  Opal.hash_get = function(hash, key) {
2286
- if (key.$$is_string) {
2287
- if ($has_own(hash.$$smap, key)) {
2288
- return hash.$$smap[key];
2289
- }
2290
- return;
2291
- }
2292
-
2293
- var key_hash, bucket;
2294
- key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash();
2295
-
2296
- if ($has_own(hash.$$map, key_hash)) {
2297
- bucket = hash.$$map[key_hash];
2298
-
2299
- while (bucket) {
2300
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2301
- 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);
2302
2293
  }
2303
- bucket = bucket.next;
2294
+ } else if (key.$$is_string) {
2295
+ return hash.get(key_hash);
2304
2296
  }
2297
+ } else if (key.$$is_string) {
2298
+ return hash.get(key.valueOf());
2305
2299
  }
2306
2300
  };
2307
2301
 
2308
- Opal.hash_delete = function(hash, key) {
2309
- var i, keys = hash.$$keys, length = keys.length, value, key_tmp;
2310
-
2311
- if (key.$$is_string) {
2312
- if (typeof key !== "string") key = key.valueOf();
2313
-
2314
- if (!$has_own(hash.$$smap, key)) {
2315
- return;
2316
- }
2317
-
2318
- for (i = 0; i < length; i++) {
2319
- key_tmp = keys[i];
2320
-
2321
- if (key_tmp.$$is_string && typeof key_tmp !== "string") {
2322
- key_tmp = key_tmp.valueOf();
2323
- }
2324
-
2325
- if (key_tmp === key) {
2326
- keys.splice(i, 1);
2327
- break;
2328
- }
2329
- }
2330
-
2331
- value = hash.$$smap[key];
2332
- 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);
2333
2306
  return value;
2334
2307
  }
2308
+ }
2335
2309
 
2336
- var key_hash = key.$hash();
2337
-
2338
- if (!$has_own(hash.$$map, key_hash)) {
2339
- return;
2340
- }
2341
-
2342
- var bucket = hash.$$map[key_hash], last_bucket;
2343
-
2344
- while (bucket) {
2345
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2346
- value = bucket.value;
2347
-
2348
- for (i = 0; i < length; i++) {
2349
- if (keys[i] === bucket) {
2350
- keys.splice(i, 1);
2351
- 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);
2352
2327
  }
2353
2328
  }
2354
-
2355
- if (last_bucket && bucket.next) {
2356
- last_bucket.next = bucket.next;
2357
- }
2358
- else if (last_bucket) {
2359
- delete last_bucket.next;
2360
- }
2361
- else if (bucket.next) {
2362
- hash.$$map[key_hash] = bucket.next;
2363
- }
2364
- else {
2365
- delete hash.$$map[key_hash];
2366
- }
2367
-
2368
- return value;
2329
+ } else if (key.$$is_string) {
2330
+ return $hash_delete_stage2(hash, key_hash);
2369
2331
  }
2370
- last_bucket = bucket;
2371
- bucket = bucket.next;
2332
+ } else if (key.$$is_string) {
2333
+ return $hash_delete_stage2(hash, key.valueOf());
2372
2334
  }
2373
2335
  };
2374
2336
 
2375
2337
  Opal.hash_rehash = function(hash) {
2376
- for (var i = 0, length = hash.$$keys.length, key_hash, bucket, last_bucket; i < length; i++) {
2338
+ var keys = hash.$$keys;
2377
2339
 
2378
- if (hash.$$keys[i].$$is_string) {
2379
- continue;
2380
- }
2340
+ if (keys)
2341
+ keys.clear();
2381
2342
 
2382
- 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
2383
2347
 
2384
- if (key_hash === hash.$$keys[i].key_hash) {
2385
- continue;
2386
- }
2348
+ var key_hash = key.$$is_string ? key.valueOf() : (hash.$$by_identity ? Opal.id(key) : key.$hash());
2387
2349
 
2388
- bucket = hash.$$map[hash.$$keys[i].key_hash];
2389
- last_bucket = undefined;
2350
+ if (!keys)
2351
+ hash.$$keys = keys = new Map();
2390
2352
 
2391
- while (bucket) {
2392
- if (bucket === hash.$$keys[i]) {
2393
- if (last_bucket && bucket.next) {
2394
- last_bucket.next = bucket.next;
2395
- }
2396
- else if (last_bucket) {
2397
- delete last_bucket.next;
2398
- }
2399
- else if (bucket.next) {
2400
- hash.$$map[hash.$$keys[i].key_hash] = bucket.next;
2401
- }
2402
- else {
2403
- delete hash.$$map[hash.$$keys[i].key_hash];
2404
- }
2405
- break;
2406
- }
2407
- last_bucket = bucket;
2408
- bucket = bucket.next;
2353
+ if (!keys.has(key_hash)) {
2354
+ keys.set(key_hash, [key]);
2355
+ return [false, false];
2409
2356
  }
2410
2357
 
2411
- hash.$$keys[i].key_hash = key_hash;
2358
+ var objects = keys.get(key_hash),
2359
+ objects_copy = $slice(objects),
2360
+ object;
2412
2361
 
2413
- if (!$has_own(hash.$$map, key_hash)) {
2414
- hash.$$map[key_hash] = hash.$$keys[i];
2415
- 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
+ }
2416
2369
  }
2417
2370
 
2418
- bucket = hash.$$map[key_hash];
2419
- last_bucket = undefined;
2371
+ objects.push(key);
2420
2372
 
2421
- while (bucket) {
2422
- if (bucket === hash.$$keys[i]) {
2423
- last_bucket = undefined;
2424
- break;
2425
- }
2426
- last_bucket = bucket;
2427
- bucket = bucket.next;
2428
- }
2373
+ return [false, false]
2374
+ });
2429
2375
 
2430
- if (last_bucket) {
2431
- last_bucket.next = hash.$$keys[i];
2432
- }
2433
- }
2376
+ return hash;
2434
2377
  };
2435
2378
 
2436
- Opal.hash = function() {
2437
- 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;
2438
2387
 
2439
2388
  if (arguments_length === 1 && arguments[0].$$is_hash) {
2440
2389
  return arguments[0];
2441
2390
  }
2442
2391
 
2443
- hash = new Opal.Hash();
2444
- Opal.hash_init(hash);
2392
+ hash = new Map();
2445
2393
 
2446
2394
  if (arguments_length === 1) {
2447
2395
  args = arguments[0];
@@ -2451,7 +2399,7 @@
2451
2399
 
2452
2400
  for (i = 0; i < length; i++) {
2453
2401
  if (args[i].length !== 2) {
2454
- $raise(Opal.ArgumentError, "value not of length 2: " + args[i].$inspect());
2402
+ $raise(Opal.ArgumentError, 'value not of length 2: ' + args[i].$inspect());
2455
2403
  }
2456
2404
 
2457
2405
  key = args[i][0];
@@ -2461,8 +2409,7 @@
2461
2409
  }
2462
2410
 
2463
2411
  return hash;
2464
- }
2465
- else {
2412
+ } else {
2466
2413
  args = arguments[0];
2467
2414
  for (key in args) {
2468
2415
  if ($has_own(args, key)) {
@@ -2477,7 +2424,7 @@
2477
2424
  }
2478
2425
 
2479
2426
  if (arguments_length % 2 !== 0) {
2480
- $raise(Opal.ArgumentError, "odd number of arguments for Hash");
2427
+ $raise(Opal.ArgumentError, 'odd number of arguments for Hash');
2481
2428
  }
2482
2429
 
2483
2430
  for (i = 0; i < arguments_length; i += 2) {
@@ -2496,15 +2443,29 @@
2496
2443
  // function.
2497
2444
  //
2498
2445
  Opal.hash2 = function(keys, smap) {
2499
- var hash = new Opal.Hash();
2500
-
2501
- hash.$$smap = smap;
2502
- hash.$$map = Object.create(null);
2503
- 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.");
2504
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
+ }
2505
2452
  return hash;
2506
2453
  };
2507
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
+
2508
2469
  // Create a new range instance with first and last values, and whether the
2509
2470
  // range excludes the last value.
2510
2471
  //
@@ -2542,7 +2503,7 @@
2542
2503
  // helper that can be used from methods
2543
2504
  function $deny_frozen_access(obj) {
2544
2505
  if (obj.$$frozen) {
2545
- $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]]));
2546
2507
  }
2547
2508
  };
2548
2509
  Opal.deny_frozen_access = $deny_frozen_access;
@@ -2667,7 +2628,7 @@
2667
2628
  if (part.ignoreCase !== ignoreCase)
2668
2629
  Opal.Kernel.$warn(
2669
2630
  "ignore case doesn't match for " + part.source.$inspect(),
2670
- Opal.hash({uplevel: 1})
2631
+ new Map([['uplevel', 1]])
2671
2632
  )
2672
2633
 
2673
2634
  part = part.source;
@@ -2918,7 +2879,7 @@
2918
2879
  // Primitives for handling parameters
2919
2880
  Opal.ensure_kwargs = function(kwargs) {
2920
2881
  if (kwargs == null) {
2921
- return Opal.hash2([], {});
2882
+ return new Map();
2922
2883
  } else if (kwargs.$$is_hash) {
2923
2884
  return kwargs;
2924
2885
  } else {
@@ -2927,10 +2888,11 @@
2927
2888
  }
2928
2889
 
2929
2890
  Opal.get_kwarg = function(kwargs, key) {
2930
- if (!$has_own(kwargs.$$smap, key)) {
2891
+ var kwarg = Opal.hash_get(kwargs, key);
2892
+ if (kwarg === undefined) {
2931
2893
  $raise(Opal.ArgumentError, 'missing keyword: '+key);
2932
2894
  }
2933
- return kwargs.$$smap[key];
2895
+ return kwarg;
2934
2896
  }
2935
2897
 
2936
2898
  // Arrays of size > 32 elements that contain only strings,
@@ -3015,10 +2977,9 @@
3015
2977
 
3016
2978
  // Foward calls to define_method on the top object to Object
3017
2979
  function top_define_method() {
3018
- var args = $slice(arguments);
3019
2980
  var block = top_define_method.$$p;
3020
2981
  top_define_method.$$p = null;
3021
- return Opal.send(_Object, 'define_method', args, block)
2982
+ return Opal.send(_Object, 'define_method', arguments, block)
3022
2983
  };
3023
2984
 
3024
2985
  // Nil
@@ -3032,7 +2993,7 @@
3032
2993
  Object.seal(nil);
3033
2994
 
3034
2995
  Opal.thrower = function(type) {
3035
- var thrower = new Error('unexpected '+type);
2996
+ var thrower = { message: 'unexpected '+type };
3036
2997
  thrower.$thrower_type = type;
3037
2998
  thrower.$throw = function(value) {
3038
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
@@ -1,3 +1,5 @@
1
+ # backtick_javascript: true
2
+
1
3
  require 'base64'
2
4
  require 'corelib/pack_unpack/format_string_parser'
3
5