opal 1.7.2 → 1.8.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) 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/CHANGELOG.md +16 -2
  8. data/UNRELEASED.md +78 -1
  9. data/benchmark-ips/bm_block_vs_yield.rb +3 -0
  10. data/benchmark-ips/bm_slice_or_not.rb +53 -0
  11. data/docs/bridging.md +112 -0
  12. data/docs/compiled_ruby.md +10 -10
  13. data/docs/getting_started.md +18 -22
  14. data/docs/index.md +1 -1
  15. data/lib/opal/builder_scheduler/prefork.rb +2 -2
  16. data/lib/opal/cache/file_cache.rb +7 -10
  17. data/lib/opal/cli_runners/chrome_cdp_interface.rb +5 -2
  18. data/lib/opal/cli_runners/firefox_cdp_interface.rb +5 -2
  19. data/lib/opal/compiler.rb +33 -1
  20. data/lib/opal/nodes/args/extract_kwoptarg.rb +2 -1
  21. data/lib/opal/nodes/call.rb +1 -1
  22. data/lib/opal/nodes/call_special.rb +71 -47
  23. data/lib/opal/nodes/hash.rb +14 -30
  24. data/lib/opal/nodes/if.rb +38 -30
  25. data/lib/opal/nodes/literal.rb +5 -1
  26. data/lib/opal/nodes/x_string.rb +13 -0
  27. data/lib/opal/parser/patch.rb +7 -0
  28. data/lib/opal/rewriters/for_rewriter.rb +36 -24
  29. data/lib/opal/source_map/file.rb +1 -1
  30. data/lib/opal/version.rb +1 -1
  31. data/opal/corelib/array/pack.rb +1 -0
  32. data/opal/corelib/array.rb +75 -47
  33. data/opal/corelib/basic_object.rb +1 -0
  34. data/opal/corelib/binding.rb +2 -0
  35. data/opal/corelib/boolean.rb +1 -0
  36. data/opal/corelib/class.rb +2 -0
  37. data/opal/corelib/comparable.rb +1 -0
  38. data/opal/corelib/complex.rb +2 -0
  39. data/opal/corelib/constants.rb +2 -2
  40. data/opal/corelib/dir.rb +2 -0
  41. data/opal/corelib/enumerable.rb +3 -2
  42. data/opal/corelib/enumerator/arithmetic_sequence.rb +2 -0
  43. data/opal/corelib/enumerator/chain.rb +1 -0
  44. data/opal/corelib/enumerator/generator.rb +1 -0
  45. data/opal/corelib/enumerator/lazy.rb +1 -0
  46. data/opal/corelib/enumerator/yielder.rb +2 -0
  47. data/opal/corelib/enumerator.rb +1 -0
  48. data/opal/corelib/error/errno.rb +2 -0
  49. data/opal/corelib/error.rb +12 -0
  50. data/opal/corelib/file.rb +1 -0
  51. data/opal/corelib/hash.rb +197 -504
  52. data/opal/corelib/helpers.rb +1 -0
  53. data/opal/corelib/io.rb +2 -0
  54. data/opal/corelib/irb.rb +2 -0
  55. data/opal/corelib/kernel/format.rb +1 -0
  56. data/opal/corelib/kernel.rb +70 -14
  57. data/opal/corelib/main.rb +2 -0
  58. data/opal/corelib/marshal/read_buffer.rb +2 -0
  59. data/opal/corelib/marshal/write_buffer.rb +2 -0
  60. data/opal/corelib/math/polyfills.rb +2 -0
  61. data/opal/corelib/math.rb +1 -0
  62. data/opal/corelib/method.rb +2 -0
  63. data/opal/corelib/module.rb +1 -0
  64. data/opal/corelib/nil.rb +3 -1
  65. data/opal/corelib/number.rb +2 -0
  66. data/opal/corelib/numeric.rb +2 -0
  67. data/opal/corelib/object_space.rb +1 -0
  68. data/opal/corelib/pack_unpack/format_string_parser.rb +2 -0
  69. data/opal/corelib/proc.rb +30 -28
  70. data/opal/corelib/process.rb +2 -0
  71. data/opal/corelib/random/formatter.rb +2 -0
  72. data/opal/corelib/random/math_random.js.rb +2 -0
  73. data/opal/corelib/random/mersenne_twister.rb +2 -0
  74. data/opal/corelib/random/seedrandom.js.rb +2 -0
  75. data/opal/corelib/random.rb +1 -0
  76. data/opal/corelib/range.rb +34 -12
  77. data/opal/corelib/rational.rb +2 -0
  78. data/opal/corelib/regexp.rb +5 -1
  79. data/opal/corelib/runtime.js +188 -224
  80. data/opal/corelib/set.rb +2 -0
  81. data/opal/corelib/string/encoding.rb +3 -0
  82. data/opal/corelib/string/unpack.rb +2 -0
  83. data/opal/corelib/string.rb +20 -12
  84. data/opal/corelib/struct.rb +3 -1
  85. data/opal/corelib/time.rb +1 -0
  86. data/opal/corelib/trace_point.rb +2 -0
  87. data/opal/corelib/unsupported.rb +2 -0
  88. data/opal/corelib/variables.rb +2 -0
  89. data/opal.gemspec +2 -2
  90. data/spec/filters/bugs/array.rb +0 -3
  91. data/spec/filters/bugs/enumerable.rb +0 -3
  92. data/spec/filters/bugs/hash.rb +0 -13
  93. data/spec/filters/bugs/integer.rb +0 -1
  94. data/spec/filters/bugs/kernel.rb +0 -39
  95. data/spec/filters/bugs/language.rb +0 -3
  96. data/spec/filters/bugs/range.rb +0 -1
  97. data/spec/filters/bugs/ruby-32.rb +0 -2
  98. data/spec/filters/bugs/string.rb +0 -1
  99. data/spec/filters/bugs/struct.rb +1 -5
  100. data/spec/filters/unsupported/hash.rb +1 -0
  101. data/spec/lib/compiler_spec.rb +24 -17
  102. data/spec/mspec-opal/formatters.rb +2 -0
  103. data/spec/mspec-opal/runner.rb +2 -0
  104. data/spec/opal/core/array/dup_spec.rb +2 -0
  105. data/spec/opal/core/exception_spec.rb +2 -0
  106. data/spec/opal/core/hash/internals_spec.rb +154 -206
  107. data/spec/opal/core/hash_spec.rb +2 -0
  108. data/spec/opal/core/iterable_props_spec.rb +2 -0
  109. data/spec/opal/core/kernel/at_exit_spec.rb +2 -0
  110. data/spec/opal/core/kernel/respond_to_spec.rb +2 -0
  111. data/spec/opal/core/language/arguments/mlhs_arg_spec.rb +2 -0
  112. data/spec/opal/core/language/case_spec.rb +13 -0
  113. data/spec/opal/core/language/safe_navigator_spec.rb +2 -0
  114. data/spec/opal/core/language/xstring_send_spec.rb +15 -0
  115. data/spec/opal/core/language/xstring_spec.rb +2 -0
  116. data/spec/opal/core/language_spec.rb +2 -0
  117. data/spec/opal/core/module_spec.rb +44 -0
  118. data/spec/opal/core/number/to_i_spec.rb +2 -0
  119. data/spec/opal/core/object_id_spec.rb +2 -0
  120. data/spec/opal/core/regexp/match_spec.rb +2 -0
  121. data/spec/opal/core/runtime/bridged_classes_spec.rb +38 -0
  122. data/spec/opal/core/runtime/constants_spec.rb +2 -0
  123. data/spec/opal/core/runtime/eval_spec.rb +2 -0
  124. data/spec/opal/core/runtime/exit_spec.rb +2 -0
  125. data/spec/opal/core/runtime/is_a_spec.rb +2 -0
  126. data/spec/opal/core/runtime/loaded_spec.rb +2 -0
  127. data/spec/opal/core/runtime/method_missing_spec.rb +2 -0
  128. data/spec/opal/core/runtime/rescue_spec.rb +2 -0
  129. data/spec/opal/core/runtime/string_spec.rb +2 -0
  130. data/spec/opal/core/runtime/truthy_spec.rb +2 -0
  131. data/spec/opal/core/runtime_spec.rb +2 -6
  132. data/spec/opal/core/string/to_sym_spec.rb +2 -0
  133. data/spec/opal/stdlib/js_spec.rb +2 -0
  134. data/spec/opal/stdlib/native/alias_native_spec.rb +2 -0
  135. data/spec/opal/stdlib/native/array_spec.rb +2 -0
  136. data/spec/opal/stdlib/native/date_spec.rb +2 -0
  137. data/spec/opal/stdlib/native/each_spec.rb +2 -0
  138. data/spec/opal/stdlib/native/element_reference_spec.rb +2 -0
  139. data/spec/opal/stdlib/native/exposure_spec.rb +2 -0
  140. data/spec/opal/stdlib/native/ext_spec.rb +2 -0
  141. data/spec/opal/stdlib/native/hash_spec.rb +30 -2
  142. data/spec/opal/stdlib/native/initialize_spec.rb +2 -0
  143. data/spec/opal/stdlib/native/method_missing_spec.rb +2 -0
  144. data/spec/opal/stdlib/native/native_alias_spec.rb +2 -0
  145. data/spec/opal/stdlib/native/native_class_spec.rb +2 -0
  146. data/spec/opal/stdlib/native/native_module_spec.rb +2 -0
  147. data/spec/opal/stdlib/native/native_reader_spec.rb +2 -0
  148. data/spec/opal/stdlib/native/native_writer_spec.rb +2 -0
  149. data/spec/opal/stdlib/native/new_spec.rb +2 -0
  150. data/spec/opal/stdlib/native/struct_spec.rb +2 -0
  151. data/spec/spec_helper.rb +2 -0
  152. data/stdlib/await.rb +1 -0
  153. data/stdlib/base64.rb +2 -0
  154. data/stdlib/bigdecimal/bignumber.js.rb +2 -0
  155. data/stdlib/bigdecimal/util.rb +1 -0
  156. data/stdlib/bigdecimal.rb +2 -0
  157. data/stdlib/buffer/array.rb +2 -0
  158. data/stdlib/buffer/view.rb +2 -0
  159. data/stdlib/buffer.rb +2 -0
  160. data/stdlib/cgi.rb +14 -0
  161. data/stdlib/console.rb +2 -0
  162. data/stdlib/date/date_time.rb +2 -0
  163. data/stdlib/date.rb +2 -0
  164. data/stdlib/delegate.rb +2 -0
  165. data/stdlib/deno/base.rb +2 -0
  166. data/stdlib/deno/file.rb +2 -0
  167. data/stdlib/erb.rb +2 -0
  168. data/stdlib/gjs/io.rb +2 -0
  169. data/stdlib/gjs/kernel.rb +2 -0
  170. data/stdlib/headless_browser/base.rb +2 -0
  171. data/stdlib/headless_browser/file.rb +1 -0
  172. data/stdlib/headless_browser.rb +1 -0
  173. data/stdlib/js.rb +2 -0
  174. data/stdlib/json.rb +9 -15
  175. data/stdlib/logger.rb +2 -0
  176. data/stdlib/nashorn/file.rb +2 -0
  177. data/stdlib/nashorn/io.rb +2 -0
  178. data/stdlib/native.rb +48 -48
  179. data/stdlib/nodejs/base.rb +2 -0
  180. data/stdlib/nodejs/dir.rb +2 -0
  181. data/stdlib/nodejs/env.rb +2 -0
  182. data/stdlib/nodejs/file.rb +2 -0
  183. data/stdlib/nodejs/fileutils.rb +2 -0
  184. data/stdlib/nodejs/io.rb +2 -0
  185. data/stdlib/nodejs/js-yaml-3-6-1.js +1 -1
  186. data/stdlib/nodejs/kernel.rb +2 -0
  187. data/stdlib/nodejs/open-uri.rb +2 -0
  188. data/stdlib/nodejs/pathname.rb +2 -0
  189. data/stdlib/nodejs/require.rb +2 -0
  190. data/stdlib/nodejs/yaml.rb +9 -3
  191. data/stdlib/opal/miniracer.rb +2 -0
  192. data/stdlib/opal-parser.rb +8 -1
  193. data/stdlib/opal-platform.rb +2 -0
  194. data/stdlib/opal-replutils.rb +2 -0
  195. data/stdlib/open-uri.rb +4 -1
  196. data/stdlib/ostruct.rb +4 -2
  197. data/stdlib/pathname.rb +2 -0
  198. data/stdlib/pp.rb +1 -0
  199. data/stdlib/promise/v2.rb +2 -0
  200. data/stdlib/quickjs/io.rb +2 -0
  201. data/stdlib/quickjs/kernel.rb +2 -0
  202. data/stdlib/quickjs.rb +2 -0
  203. data/stdlib/securerandom.rb +2 -0
  204. data/stdlib/strscan.rb +2 -0
  205. data/stdlib/time.rb +2 -0
  206. data/stdlib/uri.rb +1 -0
  207. data/tasks/performance/optimization_status.rb +2 -0
  208. data/tasks/testing.rake +1 -0
  209. data/test/nodejs/test_await.rb +1 -0
  210. data/test/nodejs/test_dir.rb +2 -0
  211. data/test/nodejs/test_error.rb +2 -0
  212. data/test/nodejs/test_file.rb +2 -0
  213. data/test/nodejs/test_string.rb +2 -0
  214. data/test/nodejs/test_yaml.rb +20 -0
  215. metadata +24 -14
  216. 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);
@@ -824,6 +830,9 @@
824
830
  // @param object [Object] the ruby object
825
831
  // @return [Class] the singleton class for object
826
832
  Opal.get_singleton_class = function(object) {
833
+ if (object.$$is_number) {
834
+ $raise(Opal.TypeError, "can't define singleton");
835
+ }
827
836
  if (object.$$meta) {
828
837
  return object.$$meta;
829
838
  }
@@ -1535,7 +1544,6 @@
1535
1544
  // @param method_name [String] The js-name of the method to stub (e.g. "$foo")
1536
1545
  // @return [undefined]
1537
1546
  Opal.stub_for = function(method_name) {
1538
-
1539
1547
  function method_missing_stub() {
1540
1548
  // Copy any given block onto the method_missing dispatcher
1541
1549
  this.$method_missing.$$p = method_missing_stub.$$p;
@@ -1544,11 +1552,8 @@
1544
1552
  method_missing_stub.$$p = null;
1545
1553
 
1546
1554
  // call method missing with correct args (remove '$' prefix on method name)
1547
- var args_ary = new Array(arguments.length);
1548
- for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = arguments[i]; }
1549
-
1550
- return this.$method_missing.apply(this, [method_name.slice(1)].concat(args_ary));
1551
- }
1555
+ return this.$method_missing.apply(this, $prepend(method_name.slice(1), arguments));
1556
+ };
1552
1557
 
1553
1558
  method_missing_stub.$$stub = true;
1554
1559
 
@@ -1690,13 +1695,6 @@
1690
1695
  }
1691
1696
  }
1692
1697
 
1693
- if (!args.$$is_array) {
1694
- var args_ary = new Array(args.length);
1695
- for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = args[i]; }
1696
-
1697
- return block.apply(null, args_ary);
1698
- }
1699
-
1700
1698
  return block.apply(null, args);
1701
1699
  };
1702
1700
 
@@ -1809,11 +1807,10 @@
1809
1807
  };
1810
1808
 
1811
1809
  // Used for extracting keyword arguments from arguments passed to
1812
- // JS function. If provided +arguments+ list doesn't have a Hash
1813
- // as a last item, returns a blank Hash.
1810
+ // JS function.
1814
1811
  //
1815
1812
  // @param parameters [Array]
1816
- // @return [Hash]
1813
+ // @return [Hash] or undefined
1817
1814
  //
1818
1815
  Opal.extract_kwargs = function(parameters) {
1819
1816
  var kwargs = parameters[parameters.length - 1];
@@ -1825,7 +1822,7 @@
1825
1822
 
1826
1823
  // Used to get a list of rest keyword arguments. Method takes the given
1827
1824
  // keyword args, i.e. the hash literal passed to the method containing all
1828
- // 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
1829
1826
  // the names of required and optional arguments defined. This method then
1830
1827
  // just returns all key/value pairs which have not been used, in a new
1831
1828
  // hash literal.
@@ -1835,19 +1832,16 @@
1835
1832
  // @return [Hash]
1836
1833
  //
1837
1834
  Opal.kwrestargs = function(given_args, used_args) {
1838
- var keys = [],
1839
- map = {},
1840
- key ,
1841
- given_map = given_args.$$smap;
1835
+ var map = new Map();
1842
1836
 
1843
- for (key in given_map) {
1837
+ Opal.hash_each(given_args, false, function(key, value) {
1844
1838
  if (!used_args[key]) {
1845
- keys.push(key);
1846
- map[key] = given_map[key];
1839
+ Opal.hash_put(map, key, value);
1847
1840
  }
1848
- }
1841
+ return [false, false];
1842
+ });
1849
1843
 
1850
- return Opal.hash2(keys, map);
1844
+ return map;
1851
1845
  };
1852
1846
 
1853
1847
  function apply_blockopts(block, blockopts) {
@@ -1866,6 +1860,11 @@
1866
1860
  }
1867
1861
  Opal.jsid = $jsid;
1868
1862
 
1863
+ function $prepend(first, second) {
1864
+ if (!second.$$is_array) second = $slice(second);
1865
+ return [first].concat(second);
1866
+ }
1867
+
1869
1868
  // Calls passed method on a ruby object with arguments and block:
1870
1869
  //
1871
1870
  // Can take a method or a method name.
@@ -1909,7 +1908,7 @@
1909
1908
  Opal.send2 = function(recv, body, method, args, block, blockopts) {
1910
1909
  if (body == null && method != null && recv.$method_missing) {
1911
1910
  body = recv.$method_missing;
1912
- args = [method].concat(args);
1911
+ args = $prepend(method, args);
1913
1912
  }
1914
1913
 
1915
1914
  apply_blockopts(block, blockopts);
@@ -2065,6 +2064,18 @@
2065
2064
  return Opal.defn(Opal.get_singleton_class(obj), jsid, body);
2066
2065
  };
2067
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
+
2068
2079
  // Called from #remove_method.
2069
2080
  Opal.rdef = function(obj, jsid) {
2070
2081
  if (!$has_own(obj.$$prototype, jsid)) {
@@ -2073,6 +2084,8 @@
2073
2084
 
2074
2085
  delete obj.$$prototype[jsid];
2075
2086
 
2087
+ remove_method_from_iclasses(obj, jsid);
2088
+
2076
2089
  if (obj.$$is_singleton) {
2077
2090
  if (obj.$$prototype.$singleton_method_removed && !obj.$$prototype.$singleton_method_removed.$$stub) {
2078
2091
  obj.$$prototype.$singleton_method_removed(jsid.substr(1));
@@ -2093,6 +2106,8 @@
2093
2106
 
2094
2107
  Opal.add_stub_for(obj.$$prototype, jsid);
2095
2108
 
2109
+ remove_method_from_iclasses(obj, jsid);
2110
+
2096
2111
  if (obj.$$is_singleton) {
2097
2112
  if (obj.$$prototype.$singleton_method_undefined && !obj.$$prototype.$singleton_method_undefined.$$stub) {
2098
2113
  obj.$$prototype.$singleton_method_undefined(jsid.substr(1));
@@ -2152,16 +2167,11 @@
2152
2167
  // We need a wrapper because otherwise properties
2153
2168
  // would be overwritten on the original body.
2154
2169
  alias = function() {
2155
- var block = alias.$$p, args, i, ii;
2156
-
2157
- args = new Array(arguments.length);
2158
- for(i = 0, ii = arguments.length; i < ii; i++) {
2159
- args[i] = arguments[i];
2160
- }
2170
+ var block = alias.$$p, i, ii;
2161
2171
 
2162
2172
  alias.$$p = null;
2163
2173
 
2164
- return Opal.send(this, body, args, block);
2174
+ return Opal.send(this, body, arguments, block);
2165
2175
  };
2166
2176
 
2167
2177
  // Assign the 'length' value with defineProperty because
@@ -2217,228 +2227,169 @@
2217
2227
  // Hashes
2218
2228
  // ------
2219
2229
 
2220
- Opal.hash_init = function(hash) {
2221
- hash.$$smap = Object.create(null);
2222
- hash.$$map = Object.create(null);
2223
- hash.$$keys = [];
2224
- };
2230
+ Opal.hash_init = function (_hash) {
2231
+ console.warn("DEPRECATION: Opal.hash_init is deprecated and is now a no-op.");
2232
+ }
2225
2233
 
2226
2234
  Opal.hash_clone = function(from_hash, to_hash) {
2227
2235
  to_hash.$$none = from_hash.$$none;
2228
2236
  to_hash.$$proc = from_hash.$$proc;
2229
2237
 
2230
- for (var i = 0, keys = from_hash.$$keys, smap = from_hash.$$smap, len = keys.length, key, value; i < len; i++) {
2231
- key = keys[i];
2232
-
2233
- if (key.$$is_string) {
2234
- value = smap[key];
2235
- } else {
2236
- value = key.value;
2237
- key = key.key;
2238
- }
2239
-
2238
+ return Opal.hash_each(from_hash, to_hash, function(key, value) {
2240
2239
  Opal.hash_put(to_hash, key, value);
2241
- }
2240
+ return [false, to_hash];
2241
+ });
2242
2242
  };
2243
2243
 
2244
2244
  Opal.hash_put = function(hash, key, value) {
2245
- if (key.$$is_string) {
2246
- if (!$has_own(hash.$$smap, key)) {
2247
- hash.$$keys.push(key);
2248
- }
2249
- hash.$$smap[key] = value;
2250
- return;
2251
- }
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();
2252
2253
 
2253
- var key_hash, bucket, last_bucket;
2254
- 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;
2255
2256
 
2256
- if (!$has_own(hash.$$map, key_hash)) {
2257
- bucket = {key: key, key_hash: key_hash, value: value};
2258
- hash.$$keys.push(bucket);
2259
- hash.$$map[key_hash] = bucket;
2260
- return;
2261
- }
2257
+ if (!keys.has(key_hash)) {
2258
+ keys.set(key_hash, [key]);
2259
+ hash.set(key, value);
2260
+ return;
2261
+ }
2262
2262
 
2263
- bucket = hash.$$map[key_hash];
2263
+ var objects = keys.get(key_hash),
2264
+ object;
2264
2265
 
2265
- while (bucket) {
2266
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2267
- last_bucket = undefined;
2268
- bucket.value = value;
2269
- 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
+ }
2270
2272
  }
2271
- last_bucket = bucket;
2272
- bucket = bucket.next;
2273
- }
2274
2273
 
2275
- if (last_bucket) {
2276
- bucket = {key: key, key_hash: key_hash, value: value};
2277
- hash.$$keys.push(bucket);
2278
- last_bucket.next = bucket;
2274
+ objects.push(key);
2275
+ hash.set(key, value);
2279
2276
  }
2280
2277
  };
2281
2278
 
2282
2279
  Opal.hash_get = function(hash, key) {
2283
- if (key.$$is_string) {
2284
- if ($has_own(hash.$$smap, key)) {
2285
- return hash.$$smap[key];
2286
- }
2287
- return;
2288
- }
2289
-
2290
- var key_hash, bucket;
2291
- key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash();
2292
-
2293
- if ($has_own(hash.$$map, key_hash)) {
2294
- bucket = hash.$$map[key_hash];
2295
-
2296
- while (bucket) {
2297
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2298
- 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);
2299
2293
  }
2300
- bucket = bucket.next;
2294
+ } else if (key.$$is_string) {
2295
+ return hash.get(key_hash);
2301
2296
  }
2297
+ } else if (key.$$is_string) {
2298
+ return hash.get(key.valueOf());
2302
2299
  }
2303
2300
  };
2304
2301
 
2305
- Opal.hash_delete = function(hash, key) {
2306
- var i, keys = hash.$$keys, length = keys.length, value, key_tmp;
2307
-
2308
- if (key.$$is_string) {
2309
- if (typeof key !== "string") key = key.valueOf();
2310
-
2311
- if (!$has_own(hash.$$smap, key)) {
2312
- return;
2313
- }
2314
-
2315
- for (i = 0; i < length; i++) {
2316
- key_tmp = keys[i];
2317
-
2318
- if (key_tmp.$$is_string && typeof key_tmp !== "string") {
2319
- key_tmp = key_tmp.valueOf();
2320
- }
2321
-
2322
- if (key_tmp === key) {
2323
- keys.splice(i, 1);
2324
- break;
2325
- }
2326
- }
2327
-
2328
- value = hash.$$smap[key];
2329
- 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);
2330
2306
  return value;
2331
2307
  }
2308
+ }
2332
2309
 
2333
- var key_hash = key.$hash();
2334
-
2335
- if (!$has_own(hash.$$map, key_hash)) {
2336
- return;
2337
- }
2338
-
2339
- var bucket = hash.$$map[key_hash], last_bucket;
2340
-
2341
- while (bucket) {
2342
- if (key === bucket.key || key['$eql?'](bucket.key)) {
2343
- value = bucket.value;
2344
-
2345
- for (i = 0; i < length; i++) {
2346
- if (keys[i] === bucket) {
2347
- keys.splice(i, 1);
2348
- 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);
2349
2327
  }
2350
2328
  }
2351
-
2352
- if (last_bucket && bucket.next) {
2353
- last_bucket.next = bucket.next;
2354
- }
2355
- else if (last_bucket) {
2356
- delete last_bucket.next;
2357
- }
2358
- else if (bucket.next) {
2359
- hash.$$map[key_hash] = bucket.next;
2360
- }
2361
- else {
2362
- delete hash.$$map[key_hash];
2363
- }
2364
-
2365
- return value;
2329
+ } else if (key.$$is_string) {
2330
+ return $hash_delete_stage2(hash, key_hash);
2366
2331
  }
2367
- last_bucket = bucket;
2368
- bucket = bucket.next;
2332
+ } else if (key.$$is_string) {
2333
+ return $hash_delete_stage2(hash, key.valueOf());
2369
2334
  }
2370
2335
  };
2371
2336
 
2372
2337
  Opal.hash_rehash = function(hash) {
2373
- for (var i = 0, length = hash.$$keys.length, key_hash, bucket, last_bucket; i < length; i++) {
2338
+ var keys = hash.$$keys;
2374
2339
 
2375
- if (hash.$$keys[i].$$is_string) {
2376
- continue;
2377
- }
2340
+ if (keys)
2341
+ keys.clear();
2378
2342
 
2379
- 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
2380
2347
 
2381
- if (key_hash === hash.$$keys[i].key_hash) {
2382
- continue;
2383
- }
2348
+ var key_hash = key.$$is_string ? key.valueOf() : (hash.$$by_identity ? Opal.id(key) : key.$hash());
2384
2349
 
2385
- bucket = hash.$$map[hash.$$keys[i].key_hash];
2386
- last_bucket = undefined;
2350
+ if (!keys)
2351
+ hash.$$keys = keys = new Map();
2387
2352
 
2388
- while (bucket) {
2389
- if (bucket === hash.$$keys[i]) {
2390
- if (last_bucket && bucket.next) {
2391
- last_bucket.next = bucket.next;
2392
- }
2393
- else if (last_bucket) {
2394
- delete last_bucket.next;
2395
- }
2396
- else if (bucket.next) {
2397
- hash.$$map[hash.$$keys[i].key_hash] = bucket.next;
2398
- }
2399
- else {
2400
- delete hash.$$map[hash.$$keys[i].key_hash];
2401
- }
2402
- break;
2403
- }
2404
- last_bucket = bucket;
2405
- bucket = bucket.next;
2353
+ if (!keys.has(key_hash)) {
2354
+ keys.set(key_hash, [key]);
2355
+ return [false, false];
2406
2356
  }
2407
2357
 
2408
- hash.$$keys[i].key_hash = key_hash;
2358
+ var objects = keys.get(key_hash),
2359
+ objects_copy = $slice(objects),
2360
+ object;
2409
2361
 
2410
- if (!$has_own(hash.$$map, key_hash)) {
2411
- hash.$$map[key_hash] = hash.$$keys[i];
2412
- 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
+ }
2413
2369
  }
2414
2370
 
2415
- bucket = hash.$$map[key_hash];
2416
- last_bucket = undefined;
2371
+ objects.push(key);
2417
2372
 
2418
- while (bucket) {
2419
- if (bucket === hash.$$keys[i]) {
2420
- last_bucket = undefined;
2421
- break;
2422
- }
2423
- last_bucket = bucket;
2424
- bucket = bucket.next;
2425
- }
2373
+ return [false, false]
2374
+ });
2426
2375
 
2427
- if (last_bucket) {
2428
- last_bucket.next = hash.$$keys[i];
2429
- }
2430
- }
2376
+ return hash;
2431
2377
  };
2432
2378
 
2433
- Opal.hash = function() {
2434
- 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;
2435
2387
 
2436
2388
  if (arguments_length === 1 && arguments[0].$$is_hash) {
2437
2389
  return arguments[0];
2438
2390
  }
2439
2391
 
2440
- hash = new Opal.Hash();
2441
- Opal.hash_init(hash);
2392
+ hash = new Map();
2442
2393
 
2443
2394
  if (arguments_length === 1) {
2444
2395
  args = arguments[0];
@@ -2448,7 +2399,7 @@
2448
2399
 
2449
2400
  for (i = 0; i < length; i++) {
2450
2401
  if (args[i].length !== 2) {
2451
- $raise(Opal.ArgumentError, "value not of length 2: " + args[i].$inspect());
2402
+ $raise(Opal.ArgumentError, 'value not of length 2: ' + args[i].$inspect());
2452
2403
  }
2453
2404
 
2454
2405
  key = args[i][0];
@@ -2458,8 +2409,7 @@
2458
2409
  }
2459
2410
 
2460
2411
  return hash;
2461
- }
2462
- else {
2412
+ } else {
2463
2413
  args = arguments[0];
2464
2414
  for (key in args) {
2465
2415
  if ($has_own(args, key)) {
@@ -2474,7 +2424,7 @@
2474
2424
  }
2475
2425
 
2476
2426
  if (arguments_length % 2 !== 0) {
2477
- $raise(Opal.ArgumentError, "odd number of arguments for Hash");
2427
+ $raise(Opal.ArgumentError, 'odd number of arguments for Hash');
2478
2428
  }
2479
2429
 
2480
2430
  for (i = 0; i < arguments_length; i += 2) {
@@ -2493,15 +2443,29 @@
2493
2443
  // function.
2494
2444
  //
2495
2445
  Opal.hash2 = function(keys, smap) {
2496
- var hash = new Opal.Hash();
2497
-
2498
- hash.$$smap = smap;
2499
- hash.$$map = Object.create(null);
2500
- 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.");
2501
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
+ }
2502
2452
  return hash;
2503
2453
  };
2504
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
+
2505
2469
  // Create a new range instance with first and last values, and whether the
2506
2470
  // range excludes the last value.
2507
2471
  //
@@ -2539,7 +2503,7 @@
2539
2503
  // helper that can be used from methods
2540
2504
  function $deny_frozen_access(obj) {
2541
2505
  if (obj.$$frozen) {
2542
- $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]]));
2543
2507
  }
2544
2508
  };
2545
2509
  Opal.deny_frozen_access = $deny_frozen_access;
@@ -2664,7 +2628,7 @@
2664
2628
  if (part.ignoreCase !== ignoreCase)
2665
2629
  Opal.Kernel.$warn(
2666
2630
  "ignore case doesn't match for " + part.source.$inspect(),
2667
- Opal.hash({uplevel: 1})
2631
+ new Map([['uplevel', 1]])
2668
2632
  )
2669
2633
 
2670
2634
  part = part.source;
@@ -2915,7 +2879,7 @@
2915
2879
  // Primitives for handling parameters
2916
2880
  Opal.ensure_kwargs = function(kwargs) {
2917
2881
  if (kwargs == null) {
2918
- return Opal.hash2([], {});
2882
+ return new Map();
2919
2883
  } else if (kwargs.$$is_hash) {
2920
2884
  return kwargs;
2921
2885
  } else {
@@ -2924,10 +2888,11 @@
2924
2888
  }
2925
2889
 
2926
2890
  Opal.get_kwarg = function(kwargs, key) {
2927
- if (!$has_own(kwargs.$$smap, key)) {
2891
+ var kwarg = Opal.hash_get(kwargs, key);
2892
+ if (kwarg === undefined) {
2928
2893
  $raise(Opal.ArgumentError, 'missing keyword: '+key);
2929
2894
  }
2930
- return kwargs.$$smap[key];
2895
+ return kwarg;
2931
2896
  }
2932
2897
 
2933
2898
  // Arrays of size > 32 elements that contain only strings,
@@ -3012,10 +2977,9 @@
3012
2977
 
3013
2978
  // Foward calls to define_method on the top object to Object
3014
2979
  function top_define_method() {
3015
- var args = $slice(arguments);
3016
2980
  var block = top_define_method.$$p;
3017
2981
  top_define_method.$$p = null;
3018
- return Opal.send(_Object, 'define_method', args, block)
2982
+ return Opal.send(_Object, 'define_method', arguments, block)
3019
2983
  };
3020
2984
 
3021
2985
  // Nil
@@ -3029,7 +2993,7 @@
3029
2993
  Object.seal(nil);
3030
2994
 
3031
2995
  Opal.thrower = function(type) {
3032
- var thrower = new Error('unexpected '+type);
2996
+ var thrower = { message: 'unexpected '+type };
3033
2997
  thrower.$thrower_type = type;
3034
2998
  thrower.$throw = function(value) {
3035
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