opal 0.8.1 → 0.9.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -2
  3. data/.gitmodules +3 -3
  4. data/.jshintrc +17 -20
  5. data/.travis.yml +22 -11
  6. data/CHANGELOG.md +51 -1
  7. data/CODE_OF_CONDUCT.md +15 -0
  8. data/CONTRIBUTING.md +125 -9
  9. data/Gemfile +1 -1
  10. data/Guardfile +2 -2
  11. data/README.md +95 -29
  12. data/Rakefile +1 -1
  13. data/benchmark/benchmarks +103 -0
  14. data/benchmark/bm_array_flatten.rb +9 -0
  15. data/benchmark/bm_array_intersection_numbers.rb +7 -0
  16. data/benchmark/bm_array_intersection_objects.rb +7 -0
  17. data/benchmark/bm_array_intersection_strings.rb +7 -0
  18. data/benchmark/bm_array_join_ary.rb +9 -0
  19. data/benchmark/bm_array_minus_numbers.rb +7 -0
  20. data/benchmark/bm_array_minus_objects.rb +7 -0
  21. data/benchmark/bm_array_minus_strings.rb +7 -0
  22. data/benchmark/bm_array_union_numbers.rb +7 -0
  23. data/benchmark/bm_array_union_objects.rb +7 -0
  24. data/benchmark/bm_array_union_strings.rb +7 -0
  25. data/benchmark/bm_array_uniq_bang_numbers.rb +5 -0
  26. data/benchmark/bm_array_uniq_bang_objects.rb +5 -0
  27. data/benchmark/bm_array_uniq_bang_strings.rb +5 -0
  28. data/benchmark/bm_array_uniq_numbers.rb +5 -0
  29. data/benchmark/bm_array_uniq_objects.rb +5 -0
  30. data/benchmark/bm_array_uniq_strings.rb +5 -0
  31. data/benchmark/bm_dispatch_bind_table.rb +57 -0
  32. data/benchmark/bm_dispatch_code_gen.rb +65 -0
  33. data/benchmark/bm_dispatch_code_gen_if.rb +64 -0
  34. data/benchmark/bm_dispatch_hardcoded.rb +44 -0
  35. data/benchmark/bm_dispatch_send.rb +38 -0
  36. data/benchmark/bm_dispatch_send_table.rb +57 -0
  37. data/benchmark/bm_hash_assoc_object.rb +11 -0
  38. data/benchmark/bm_hash_assoc_string.rb +9 -0
  39. data/benchmark/bm_hash_clone_object.rb +9 -0
  40. data/benchmark/bm_hash_clone_string.rb +9 -0
  41. data/benchmark/bm_hash_delete_object.rb +11 -0
  42. data/benchmark/bm_hash_delete_string.rb +9 -0
  43. data/benchmark/bm_hash_each_key_object.rb +9 -0
  44. data/benchmark/bm_hash_each_key_string.rb +9 -0
  45. data/benchmark/bm_hash_each_object.rb +9 -0
  46. data/benchmark/bm_hash_each_string.rb +9 -0
  47. data/benchmark/bm_hash_each_value_object.rb +9 -0
  48. data/benchmark/bm_hash_each_value_string.rb +9 -0
  49. data/benchmark/bm_hash_element_reference_object.rb +11 -0
  50. data/benchmark/bm_hash_element_reference_string.rb +9 -0
  51. data/benchmark/bm_hash_element_set_object.rb +5 -0
  52. data/benchmark/bm_hash_element_set_string.rb +5 -0
  53. data/benchmark/bm_hash_equal_value_object.rb +14 -0
  54. data/benchmark/bm_hash_equal_value_string.rb +11 -0
  55. data/benchmark/bm_hash_fetch_object.rb +11 -0
  56. data/benchmark/bm_hash_fetch_string.rb +9 -0
  57. data/benchmark/bm_hash_flatten_object.rb +9 -0
  58. data/benchmark/bm_hash_flatten_string.rb +9 -0
  59. data/benchmark/bm_hash_has_key_object.rb +11 -0
  60. data/benchmark/bm_hash_has_key_string.rb +9 -0
  61. data/benchmark/bm_hash_has_value_object.rb +9 -0
  62. data/benchmark/bm_hash_has_value_string.rb +9 -0
  63. data/benchmark/bm_hash_hash_object.rb +9 -0
  64. data/benchmark/bm_hash_hash_string.rb +9 -0
  65. data/benchmark/bm_hash_inspect_object.rb +9 -0
  66. data/benchmark/bm_hash_inspect_string.rb +9 -0
  67. data/benchmark/bm_hash_invert_object.rb +9 -0
  68. data/benchmark/bm_hash_invert_string.rb +9 -0
  69. data/benchmark/bm_hash_keep_if_object.rb +9 -0
  70. data/benchmark/bm_hash_keep_if_string.rb +9 -0
  71. data/benchmark/bm_hash_key_object.rb +9 -0
  72. data/benchmark/bm_hash_key_string.rb +9 -0
  73. data/benchmark/bm_hash_keys_object.rb +9 -0
  74. data/benchmark/bm_hash_keys_string.rb +9 -0
  75. data/benchmark/bm_hash_literal_mixed_large.rb +3 -0
  76. data/benchmark/bm_hash_literal_mixed_small.rb +3 -0
  77. data/benchmark/bm_hash_literal_object_large.rb +4 -0
  78. data/benchmark/bm_hash_literal_object_small.rb +3 -0
  79. data/benchmark/bm_hash_literal_string_large.rb +4 -0
  80. data/benchmark/bm_hash_literal_string_small.rb +3 -0
  81. data/benchmark/bm_hash_merge_object.rb +22 -0
  82. data/benchmark/bm_hash_merge_string.rb +18 -0
  83. data/benchmark/bm_hash_rassoc_object.rb +9 -0
  84. data/benchmark/bm_hash_rassoc_string.rb +9 -0
  85. data/benchmark/bm_hash_rehash_object.rb +9 -0
  86. data/benchmark/bm_hash_rehash_string.rb +9 -0
  87. data/benchmark/bm_hash_reject_bang_object.rb +9 -0
  88. data/benchmark/bm_hash_reject_bang_string.rb +9 -0
  89. data/benchmark/bm_hash_reject_object.rb +9 -0
  90. data/benchmark/bm_hash_reject_string.rb +9 -0
  91. data/benchmark/bm_hash_replace_object.rb +18 -0
  92. data/benchmark/bm_hash_replace_string.rb +14 -0
  93. data/benchmark/bm_hash_select_bang_object.rb +9 -0
  94. data/benchmark/bm_hash_select_bang_string.rb +9 -0
  95. data/benchmark/bm_hash_select_object.rb +9 -0
  96. data/benchmark/bm_hash_select_string.rb +9 -0
  97. data/benchmark/bm_hash_shift_object.rb +10 -0
  98. data/benchmark/bm_hash_shift_string.rb +10 -0
  99. data/benchmark/bm_hash_to_a_object.rb +9 -0
  100. data/benchmark/bm_hash_to_a_string.rb +9 -0
  101. data/benchmark/bm_hash_to_h_object.rb +10 -0
  102. data/benchmark/bm_hash_to_h_string.rb +10 -0
  103. data/benchmark/bm_hash_values_object.rb +9 -0
  104. data/benchmark/bm_hash_values_string.rb +9 -0
  105. data/benchmark/run.rb +48 -0
  106. data/bin/opal-mspec +1 -1
  107. data/bin/opal-repl +4 -4
  108. data/docs/compiled_ruby.md +214 -56
  109. data/docs/configuring_gems.md +2 -2
  110. data/docs/faq.md +2 -2
  111. data/docs/getting_started.md +19 -2
  112. data/docs/jquery.md +5 -5
  113. data/docs/opal_parser.md +53 -0
  114. data/docs/unsupported_features.md +2 -2
  115. data/docs/upgrading.md +22 -0
  116. data/docs/using_sprockets.md +15 -0
  117. data/examples/rack/config.ru +13 -0
  118. data/examples/sinatra/config.ru +4 -5
  119. data/lib/mspec/opal/runner.rb +54 -11
  120. data/lib/opal.rb +1 -1
  121. data/lib/opal/builder.rb +1 -1
  122. data/lib/opal/builder_processors.rb +1 -1
  123. data/lib/opal/cli.rb +17 -13
  124. data/lib/opal/cli_options.rb +1 -1
  125. data/lib/opal/compiler.rb +12 -0
  126. data/lib/opal/config.rb +4 -0
  127. data/lib/opal/nodes/arglist.rb +5 -7
  128. data/lib/opal/nodes/call.rb +6 -1
  129. data/lib/opal/nodes/call_special.rb +74 -0
  130. data/lib/opal/nodes/def.rb +35 -28
  131. data/lib/opal/nodes/definitions.rb +3 -5
  132. data/lib/opal/nodes/for.rb +13 -0
  133. data/lib/opal/nodes/helpers.rb +15 -1
  134. data/lib/opal/nodes/if.rb +5 -5
  135. data/lib/opal/nodes/iter.rb +6 -1
  136. data/lib/opal/nodes/literal.rb +1 -1
  137. data/lib/opal/nodes/logic.rb +2 -2
  138. data/lib/opal/nodes/masgn.rb +1 -2
  139. data/lib/opal/nodes/module.rb +2 -1
  140. data/lib/opal/nodes/rescue.rb +10 -1
  141. data/lib/opal/nodes/scope.rb +8 -2
  142. data/lib/opal/nodes/singleton_class.rb +1 -1
  143. data/lib/opal/nodes/top.rb +11 -0
  144. data/lib/opal/nodes/variables.rb +4 -4
  145. data/lib/opal/parser.rb +21 -3
  146. data/lib/opal/parser/grammar.rb +3115 -2961
  147. data/lib/opal/parser/grammar.y +29 -6
  148. data/lib/opal/parser/lexer.rb +18 -8
  149. data/lib/opal/sprockets.rb +85 -0
  150. data/lib/opal/sprockets/processor.rb +11 -35
  151. data/lib/opal/sprockets/server.rb +3 -15
  152. data/lib/opal/version.rb +2 -2
  153. data/opal.gemspec +4 -4
  154. data/opal/README.md +9 -0
  155. data/opal/corelib/array.rb +433 -181
  156. data/opal/corelib/basic_object.rb +48 -4
  157. data/opal/corelib/boolean.rb +15 -6
  158. data/opal/corelib/class.rb +6 -5
  159. data/opal/corelib/comparable.rb +12 -0
  160. data/opal/corelib/complex.rb +282 -0
  161. data/opal/corelib/constants.rb +9 -0
  162. data/opal/corelib/enumerable.rb +83 -34
  163. data/opal/corelib/enumerator.rb +3 -1
  164. data/opal/corelib/error.rb +49 -10
  165. data/opal/corelib/file.rb +1 -0
  166. data/opal/corelib/hash.rb +353 -577
  167. data/opal/corelib/helpers.rb +20 -0
  168. data/opal/corelib/kernel.rb +114 -59
  169. data/opal/corelib/math.rb +470 -0
  170. data/opal/corelib/method.rb +11 -2
  171. data/opal/corelib/module.rb +96 -96
  172. data/opal/corelib/{nil_class.rb → nil.rb} +20 -1
  173. data/opal/corelib/number.rb +751 -0
  174. data/opal/corelib/numeric.rb +77 -437
  175. data/opal/corelib/proc.rb +81 -1
  176. data/opal/corelib/process.rb +27 -0
  177. data/opal/corelib/rational.rb +358 -0
  178. data/opal/corelib/regexp.rb +156 -27
  179. data/opal/corelib/runtime.js +724 -335
  180. data/opal/corelib/string.rb +93 -104
  181. data/opal/corelib/string/encoding.rb +177 -0
  182. data/opal/corelib/string/inheritance.rb +2 -0
  183. data/opal/corelib/struct.rb +105 -18
  184. data/opal/corelib/time.rb +267 -146
  185. data/opal/corelib/unsupported.rb +216 -0
  186. data/opal/corelib/variables.rb +0 -6
  187. data/opal/opal.rb +8 -22
  188. data/opal/opal/base.rb +9 -0
  189. data/opal/opal/mini.rb +17 -0
  190. data/spec/README.md +1 -1
  191. data/spec/filters/bugs/array.rb +38 -136
  192. data/spec/filters/bugs/{basic_object.rb → basicobject.rb} +14 -15
  193. data/spec/filters/bugs/class.rb +6 -12
  194. data/spec/filters/bugs/complex.rb +3 -0
  195. data/spec/filters/bugs/date.rb +162 -10
  196. data/spec/filters/bugs/enumerable.rb +31 -58
  197. data/spec/filters/bugs/enumerator.rb +42 -0
  198. data/spec/filters/bugs/exception.rb +66 -10
  199. data/spec/filters/bugs/float.rb +17 -0
  200. data/spec/filters/bugs/hash.rb +11 -97
  201. data/spec/filters/bugs/inheritance.rb +5 -0
  202. data/spec/filters/bugs/integer.rb +28 -0
  203. data/spec/filters/bugs/kernel.rb +304 -12
  204. data/spec/filters/bugs/language.rb +133 -399
  205. data/spec/filters/bugs/language_opal.rb +88 -0
  206. data/spec/filters/bugs/module.rb +203 -62
  207. data/spec/filters/bugs/numeric.rb +32 -0
  208. data/spec/filters/bugs/proc.rb +39 -0
  209. data/spec/filters/bugs/range.rb +148 -0
  210. data/spec/filters/bugs/regexp.rb +168 -0
  211. data/spec/filters/bugs/set.rb +46 -3
  212. data/spec/filters/bugs/singleton.rb +1 -2
  213. data/spec/filters/bugs/string.rb +59 -90
  214. data/spec/filters/bugs/strscan.rb +80 -0
  215. data/spec/filters/bugs/struct.rb +10 -20
  216. data/spec/filters/bugs/time.rb +17 -184
  217. data/spec/filters/bugs/unboundmethod.rb +22 -0
  218. data/spec/filters/unsupported/array.rb +163 -0
  219. data/spec/filters/unsupported/basicobject.rb +14 -0
  220. data/spec/filters/unsupported/bignum.rb +46 -0
  221. data/spec/filters/unsupported/class.rb +4 -0
  222. data/spec/filters/unsupported/delegator.rb +5 -0
  223. data/spec/filters/unsupported/enumerable.rb +11 -0
  224. data/spec/filters/unsupported/enumerator.rb +8 -9
  225. data/spec/filters/unsupported/fixnum.rb +14 -0
  226. data/spec/filters/unsupported/float.rb +41 -7
  227. data/spec/filters/unsupported/freeze.rb +45 -0
  228. data/spec/filters/unsupported/hash.rb +50 -0
  229. data/spec/filters/unsupported/integer.rb +3 -0
  230. data/spec/filters/unsupported/kernel.rb +31 -0
  231. data/spec/filters/unsupported/language.rb +17 -0
  232. data/spec/filters/unsupported/matchdata.rb +30 -0
  233. data/spec/filters/unsupported/math.rb +3 -0
  234. data/spec/filters/unsupported/module.rb +5 -3
  235. data/spec/filters/unsupported/pathname.rb +3 -0
  236. data/spec/filters/unsupported/privacy.rb +136 -0
  237. data/spec/filters/unsupported/proc.rb +3 -0
  238. data/spec/filters/unsupported/regexp.rb +59 -0
  239. data/spec/filters/unsupported/set.rb +4 -0
  240. data/spec/filters/unsupported/{marshal.rb → singleton.rb} +4 -2
  241. data/spec/filters/unsupported/{mutable_strings.rb → string.rb} +456 -336
  242. data/spec/filters/unsupported/struct.rb +3 -0
  243. data/spec/filters/unsupported/symbol.rb +5 -0
  244. data/spec/filters/unsupported/taint.rb +16 -0
  245. data/spec/filters/unsupported/thread.rb +5 -0
  246. data/spec/filters/unsupported/time.rb +197 -16
  247. data/spec/lib/cli_spec.rb +14 -4
  248. data/spec/lib/compiler_spec.rb +9 -1
  249. data/spec/lib/parser/call_spec.rb +18 -0
  250. data/spec/lib/parser/not_spec.rb +2 -8
  251. data/spec/lib/sprockets_spec.rb +24 -0
  252. data/spec/opal/core/array/intersection_spec.rb +38 -0
  253. data/spec/opal/core/array/minus_spec.rb +38 -0
  254. data/spec/opal/core/array/union_spec.rb +38 -0
  255. data/spec/opal/core/array/uniq_spec.rb +49 -0
  256. data/spec/opal/core/exception_spec.rb +7 -0
  257. data/spec/opal/core/fixtures/require_tree_with_dot/file 1.rb +1 -0
  258. data/spec/opal/core/fixtures/require_tree_with_dot/file 2.rb +1 -0
  259. data/spec/opal/core/fixtures/require_tree_with_dot/file 3.rb +1 -0
  260. data/spec/opal/core/fixtures/require_tree_with_dot/index.rb +3 -0
  261. data/spec/opal/core/hash/internals_spec.rb +332 -0
  262. data/spec/opal/core/helpers_spec.rb +14 -0
  263. data/spec/opal/core/kernel/freeze_spec.rb +1 -1
  264. data/spec/opal/core/kernel/raise_spec.rb +13 -0
  265. data/spec/opal/core/kernel/require_tree_spec.rb +9 -0
  266. data/spec/opal/core/language/class_spec.rb +55 -0
  267. data/spec/opal/core/language/fixtures/send.rb +1 -0
  268. data/spec/opal/core/language/keyword_arguments_spec.rb +11 -0
  269. data/spec/opal/core/language/send_spec.rb +5 -0
  270. data/spec/opal/core/method/to_proc_spec.rb +28 -0
  271. data/spec/opal/core/module/name_spec.rb +0 -17
  272. data/spec/opal/core/runtime/bridged_classes_spec.rb +2 -2
  273. data/spec/opal/core/runtime/eval_spec.rb +1 -1
  274. data/spec/opal/core/runtime/method_missing_spec.rb +6 -0
  275. data/spec/opal/core/runtime_spec.rb +51 -0
  276. data/spec/opal/stdlib/js_spec.rb +66 -0
  277. data/spec/opal/stdlib/native/hash_spec.rb +36 -0
  278. data/spec/rubyspecs +152 -273
  279. data/spec/spec_helper.rb +10 -11
  280. data/stdlib/base64.rb +9 -9
  281. data/stdlib/benchmark.rb +551 -4
  282. data/stdlib/console.rb +94 -0
  283. data/stdlib/date.rb +1 -1
  284. data/stdlib/encoding.rb +1 -170
  285. data/stdlib/js.rb +56 -0
  286. data/stdlib/json.rb +9 -14
  287. data/stdlib/math.rb +1 -370
  288. data/stdlib/native.rb +133 -63
  289. data/stdlib/nodejs/file.rb +5 -0
  290. data/stdlib/nodejs/fileutils.rb +13 -6
  291. data/stdlib/nodejs/node_modules/js-yaml/node_modules/argparse/README.md +1 -1
  292. data/stdlib/opal-parser.rb +1 -2
  293. data/stdlib/ostruct.rb +65 -6
  294. data/stdlib/pp.rb +2 -4
  295. data/stdlib/rbconfig.rb +1 -3
  296. data/stdlib/strscan.rb +164 -28
  297. data/tasks/benchmarking.rake +88 -0
  298. data/tasks/testing.rake +181 -55
  299. data/{lib/mspec/opal/special_calls.rb → tasks/testing/mspec_special_calls.rb} +1 -1
  300. data/{lib/mspec/opal/sprockets.js → tasks/testing/phantomjs1-sprockets.js} +17 -6
  301. data/test/opal/test_keyword.rb +590 -0
  302. data/vendored-minitest/minitest.rb +2 -2
  303. data/vendored-minitest/test/unit.rb +5 -0
  304. metadata +229 -62
  305. data/benchmarks/operators.rb +0 -11
  306. data/benchmarks/prova.js.rb +0 -13
  307. data/docs/libraries.md +0 -36
  308. data/lib/mspec/opal/new.html.erb +0 -1
  309. data/lib/mspec/opal/rake_task.rb +0 -248
  310. data/opal/corelib/match_data.rb +0 -128
  311. data/spec/filters/bugs/math.rb +0 -95
  312. data/spec/filters/bugs/nil.rb +0 -7
  313. data/spec/filters/bugs/opal.rb +0 -9
  314. data/spec/filters/bugs/regular_expressions.rb +0 -41
  315. data/spec/filters/bugs/stringscanner.rb +0 -33
  316. data/spec/filters/unsupported/encoding.rb +0 -102
  317. data/spec/filters/unsupported/frozen.rb +0 -92
  318. data/spec/filters/unsupported/hash_compare_by_identity.rb +0 -16
  319. data/spec/filters/unsupported/integer_size.rb +0 -59
  320. data/spec/filters/unsupported/method_added.rb +0 -10
  321. data/spec/filters/unsupported/private_constants.rb +0 -30
  322. data/spec/filters/unsupported/private_methods.rb +0 -55
  323. data/spec/filters/unsupported/random.rb +0 -4
  324. data/spec/filters/unsupported/rational_numbers.rb +0 -4
  325. data/spec/filters/unsupported/regular_expressions.rb +0 -137
  326. data/spec/filters/unsupported/ruby_exe.rb +0 -5
  327. data/spec/filters/unsupported/symbols.rb +0 -17
  328. data/spec/filters/unsupported/tainted.rb +0 -180
  329. data/spec/filters/unsupported/trusted.rb +0 -88
  330. data/stdlib/process.rb +0 -10
  331. data/tasks/documenting.rake +0 -37
@@ -8,14 +8,14 @@ token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
8
8
  k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT
9
9
  tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT
10
10
  tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ tGEQ tLEQ tANDOP
11
- tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT
11
+ tOROP tMATCH tNMATCH tJSDOT tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT
12
12
  tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN tLPAREN2 tRPAREN tLPAREN_ARG
13
13
  ARRAY_BEG tRBRACK tLBRACE tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2
14
14
  tTILDE tPERCENT tDIVIDE tPLUS tMINUS tLT tGT tPIPE tBANG tCARET
15
15
  tLCURLY tRCURLY tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG
16
16
  tWORDS_BEG tAWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END tSTRING
17
17
  tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG
18
- tLBRACK2 tLBRACK tDSTAR
18
+ tLBRACK2 tLBRACK tJSLBRACK tDSTAR
19
19
 
20
20
  prechigh
21
21
  right tBANG tTILDE tUPLUS
@@ -150,6 +150,7 @@ rule
150
150
  result = new_op_asgn val[1], val[0], val[2]
151
151
  }
152
152
  | primary_value tLBRACK2 aref_args tRBRACK tOP_ASGN command_call
153
+ | primary_value tJSLBRACK aref_args tRBRACK tOP_ASGN command_call
153
154
  | primary_value tDOT tIDENTIFIER tOP_ASGN command_call
154
155
  {
155
156
  result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
@@ -208,6 +209,7 @@ rule
208
209
  }
209
210
 
210
211
  block_command: block_call
212
+ | block_call tJSDOT operation2 command_args
211
213
  | block_call tDOT operation2 command_args
212
214
  | block_call tCOLON2 operation2 command_args
213
215
 
@@ -218,6 +220,11 @@ rule
218
220
  result = new_call(nil, val[0], val[1])
219
221
  }
220
222
  | operation command_args cmd_brace_block
223
+ | primary_value tJSDOT operation2 command_args =tLOWEST
224
+ {
225
+ result = new_js_call(val[0], val[2], val[3])
226
+ }
227
+ | primary_value tJSDOT operation2 command_args cmd_brace_block
221
228
  | primary_value tDOT operation2 command_args =tLOWEST
222
229
  {
223
230
  result = new_call(val[0], val[2], val[3])
@@ -327,6 +334,10 @@ rule
327
334
  {
328
335
  result = new_assignable val[0]
329
336
  }
337
+ | primary_value tJSLBRACK aref_args tRBRACK
338
+ {
339
+ result = new_js_attrasgn(val[0], val[2])
340
+ }
330
341
  | primary_value tLBRACK2 aref_args tRBRACK
331
342
  {
332
343
  result = new_attrasgn(val[0], :[]=, val[2])
@@ -428,6 +439,10 @@ rule
428
439
  {
429
440
  result = new_op_asgn1(val[0], val[2], val[4], val[5])
430
441
  }
442
+ | primary_value tJSLBRACK aref_args tRBRACK tOP_ASGN arg
443
+ {
444
+ raise ".JS[...] #{val[4]} is not supported"
445
+ }
431
446
  | primary_value tDOT tIDENTIFIER tOP_ASGN arg
432
447
  {
433
448
  result = s(:op_asgn2, val[0], op_to_setter(val[2]), value(val[3]).to_sym, val[4])
@@ -537,8 +552,7 @@ rule
537
552
  }
538
553
  | arg tNEQ arg
539
554
  {
540
- result = new_unary_call(['!', []], new_binary_call(
541
- val[0], ['==', []], val[2]))
555
+ result = new_binary_call(val[0], val[1], val[2])
542
556
  }
543
557
  | arg tMATCH arg
544
558
  {
@@ -546,8 +560,7 @@ rule
546
560
  }
547
561
  | arg tNMATCH arg
548
562
  {
549
- result = new_not(val[1], new_binary_call(
550
- val[0], ['=~', []], val[2]))
563
+ result = new_binary_call(val[0], val[1], val[2])
551
564
  }
552
565
  | tBANG arg
553
566
  {
@@ -657,6 +670,7 @@ rule
657
670
  {
658
671
  result = val[0]
659
672
  result << new_hash(nil, val[2], nil)
673
+ result << val[3] if val[3]
660
674
  }
661
675
  | block_arg
662
676
  {
@@ -769,6 +783,10 @@ rule
769
783
  {
770
784
  result = new_call val[0], [:[], []], val[2]
771
785
  }
786
+ | primary_value tJSLBRACK aref_args tRBRACK
787
+ {
788
+ result = new_js_call val[0], [:[], []], val[2]
789
+ }
772
790
  | tLBRACK aref_args tRBRACK
773
791
  {
774
792
  result = new_array(val[0], val[1], val[2])
@@ -1098,6 +1116,7 @@ opt_block_args_tail: tCOMMA block_args_tail
1098
1116
  val[0] << val[1]
1099
1117
  result = val[0]
1100
1118
  }
1119
+ | block_call tJSDOT operation2 opt_paren_args
1101
1120
  | block_call tDOT operation2 opt_paren_args
1102
1121
  | block_call tCOLON2 operation2 opt_paren_args
1103
1122
 
@@ -1109,6 +1128,10 @@ opt_block_args_tail: tCOMMA block_args_tail
1109
1128
  {
1110
1129
  result = new_call(val[0], val[2], val[3])
1111
1130
  }
1131
+ | primary_value tJSDOT operation2 opt_paren_args
1132
+ {
1133
+ result = new_js_call(val[0], val[2], val[3])
1134
+ }
1112
1135
  | primary_value tDOT paren_args
1113
1136
  {
1114
1137
  result = new_call(val[0], [:call, []], val[2])
@@ -987,6 +987,16 @@ module Opal
987
987
  @lex_state = :expr_beg
988
988
  return :tDOT2
989
989
 
990
+ elsif @lex_state != :expr_fname && scan(/\.JS\[/)
991
+ @lex_state = :expr_beg
992
+ cond_push 0
993
+ cmdarg_push 0
994
+ return :tJSLBRACK
995
+
996
+ elsif @lex_state != :expr_fname && scan(/\.JS\./)
997
+ @lex_state = :expr_dot
998
+ return :tJSDOT
999
+
990
1000
  elsif scan(/\./)
991
1001
  @lex_state = :expr_dot unless @lex_state == :expr_fname
992
1002
  return :tDOT
@@ -1031,16 +1041,16 @@ module Opal
1031
1041
  self.set_arg_state
1032
1042
  return :tCARET
1033
1043
 
1034
- elsif check(/\</)
1035
- if scan(/\<\<\=/)
1044
+ elsif check(/</)
1045
+ if scan(/<<\=/)
1036
1046
  @lex_state = :expr_beg
1037
1047
  return new_op_asgn('<<')
1038
1048
 
1039
- elsif scan(/\<\</)
1049
+ elsif scan(/<</)
1040
1050
  if after_operator?
1041
1051
  @lex_state = :expr_arg
1042
1052
  return :tLSHFT
1043
- elsif !after_operator? && !end? && (!arg? || @space_seen)
1053
+ elsif !after_operator? && !end? && (!arg? || @space_seen) && @lex_state != :expr_class
1044
1054
  if token = heredoc_identifier
1045
1055
  return token
1046
1056
  end
@@ -1050,7 +1060,7 @@ module Opal
1050
1060
  end
1051
1061
  @lex_state = :expr_beg
1052
1062
  return :tLSHFT
1053
- elsif scan(/\<\=\>/)
1063
+ elsif scan(/<\=\>/)
1054
1064
  if after_operator?
1055
1065
  @lex_state = :expr_arg
1056
1066
  else
@@ -1062,11 +1072,11 @@ module Opal
1062
1072
  end
1063
1073
 
1064
1074
  return :tCMP
1065
- elsif scan(/\<\=/)
1075
+ elsif scan(/<\=/)
1066
1076
  self.set_arg_state
1067
1077
  return :tLEQ
1068
1078
 
1069
- elsif scan(/\</)
1079
+ elsif scan(/</)
1070
1080
  self.set_arg_state
1071
1081
  return :tLT
1072
1082
  end
@@ -1228,7 +1238,7 @@ module Opal
1228
1238
  elsif check(/[0-9]/)
1229
1239
  return process_numeric
1230
1240
 
1231
- elsif scan(/(\w)+[\?\!]?/)
1241
+ elsif scan(/(\w)+(\?|(\!(?!=)))?/)
1232
1242
  return process_identifier scanner.matched, cmd_start
1233
1243
  end
1234
1244
 
@@ -1,3 +1,88 @@
1
1
  require 'opal/sprockets/processor'
2
2
  require 'opal/sprockets/erb'
3
3
  require 'opal/sprockets/server'
4
+
5
+ module Opal
6
+ module Sprockets
7
+ # Public: Bootstraps modules loaded by sprockets on `Opal.modules` marking any
8
+ # non-Opal asset as already loaded.
9
+ #
10
+ # name - The name of the main asset to be loaded (with or without ext)
11
+ # sprockets - A Sprockets::Environment instance
12
+ #
13
+ # Example
14
+ #
15
+ # Opal::Sprockets.load_asset(Rails.application.assets, 'application')
16
+ #
17
+ # Will output the following JavaScript:
18
+ #
19
+ # if (typeof(Opal) !== 'undefined') {
20
+ # Opal.mark_as_loaded("opal");
21
+ # Opal.mark_as_loaded("jquery.self");
22
+ # Opal.load("application");
23
+ # }
24
+ #
25
+ # Returns a String containing JavaScript code.
26
+ def self.load_asset(name, sprockets)
27
+ asset = sprockets[name.sub(/(\.(js|rb|opal))*#{REGEXP_END}/, '.js')]
28
+ return '' if asset.nil?
29
+
30
+ opal_extnames = sprockets.engines.map do |ext, engine|
31
+ ext if engine <= ::Opal::Processor
32
+ end.compact
33
+
34
+ module_name = -> asset { asset.logical_path.sub(/\.js#{REGEXP_END}/, '') }
35
+ path_extnames = -> path { File.basename(path).scan(/\.[^.]+/) }
36
+ mark_loaded = -> paths { "Opal.loaded([#{paths.map(&:inspect).join(',')}]);" }
37
+ processed_by_opal = -> asset { (path_extnames[asset.pathname] & opal_extnames).any? }
38
+ stubbed_files = ::Opal::Processor.stubbed_files
39
+
40
+ non_opal_assets = ([asset]+asset.dependencies)
41
+ .select { |asset| not(processed_by_opal[asset]) }
42
+ .map { |asset| module_name[asset] }
43
+
44
+ loaded = ['opal'] + non_opal_assets + stubbed_files.to_a
45
+
46
+ if processed_by_opal[asset]
47
+ load_asset_code = "Opal.load(#{module_name[asset].inspect});"
48
+ end
49
+
50
+
51
+ "if (typeof(Opal) !== 'undefined') { "\
52
+ "#{mark_loaded[loaded]} "\
53
+ "#{load_asset_code} "\
54
+ "}"
55
+ end
56
+
57
+ # Public: Generate a `<script>` tag for Opal assets.
58
+ #
59
+ # name - The name of the asset to be loaded
60
+ # options - (default: {}):
61
+ # :sprockets - A Sprockets::Environment instance
62
+ # :prefix - The prefix String at which is mounted Sprockets
63
+ # :debug - Wether to enable debug mode along with sourcemaps support
64
+ #
65
+ # Returns a string of HTML code containing `<script>` tags.
66
+ def self.javascript_include_tag(name, options = {})
67
+ sprockets = options.fetch(:sprockets)
68
+ prefix = options.fetch(:prefix)
69
+ debug = options.fetch(:debug)
70
+
71
+ asset = sprockets[name]
72
+ raise "Cannot find asset: #{name}" if asset.nil?
73
+ scripts = []
74
+
75
+ if debug
76
+ asset.to_a.map do |dependency|
77
+ scripts << %{<script src="#{prefix}/#{dependency.logical_path}?body=1"></script>}
78
+ end
79
+ else
80
+ scripts << %{<script src="#{prefix}/#{name}.js"></script>}
81
+ end
82
+
83
+ scripts << %{<script>#{::Opal::Sprockets.load_asset(name, sprockets)}</script>}
84
+
85
+ scripts.join "\n"
86
+ end
87
+ end
88
+ end
@@ -8,12 +8,12 @@ require 'opal/sprockets/source_map_server'
8
8
  $OPAL_SOURCE_MAPS = {}
9
9
 
10
10
  module Opal
11
- # The Processor class is used to make ruby files (with rb or opal extensions)
12
- # available to any sprockets based server. Processor will then get passed any
13
- # ruby source file to build.
11
+ # Internal: The Processor class is used to make ruby files (with .rb or .opal
12
+ # extensions) available to any sprockets based server. Processor will then
13
+ # get passed any ruby source file to build.
14
14
  class Processor < TiltTemplate
15
- # DEPRECATED:
16
- # Support legacy accessors to default options, now moved to Opal::Config
15
+ # Deprecated: Support legacy accessors to default options,
16
+ # now moved to Opal::Config
17
17
  Opal::Config.default_config.keys.each do |config_option|
18
18
  define_singleton_method(config_option) { Opal::Config.config[config_option] }
19
19
  define_singleton_method("#{config_option}=") { |value| Opal::Config.config[config_option] = value }
@@ -36,7 +36,8 @@ module Opal
36
36
  # In Sprockets 3 logical_path has an odd behavior when the filename is "index"
37
37
  # thus we need to bake our own logical_path
38
38
  filename = context.respond_to?(:filename) ? context.filename : context.pathname.to_s
39
- logical_path = filename.gsub(%r{^#{context.root_path}/?(.*?)#{sprockets_extnames_regexp}}, '\1')
39
+ root_path_regexp = Regexp.escape(context.root_path)
40
+ logical_path = filename.gsub(%r{^#{root_path_regexp}/?(.*?)#{sprockets_extnames_regexp}}, '\1')
40
41
 
41
42
  compiler_options = self.compiler_options.merge(file: logical_path)
42
43
 
@@ -78,6 +79,8 @@ module Opal
78
79
  end
79
80
  end
80
81
 
82
+ # Internal: Add files required with `require_tree` as asset dependencies.
83
+ #
81
84
  # Mimics (v2) Sprockets::DirectiveProcessor#process_require_tree_directive
82
85
  def process_required_trees(required_trees, context)
83
86
  return if required_trees.empty?
@@ -122,36 +125,9 @@ module Opal
122
125
  end
123
126
  end
124
127
 
128
+ # Deprecated: Moved to Opal::Sprockets.load_asset(sprockets, name)
125
129
  def self.load_asset_code(sprockets, name)
126
- asset = sprockets[name.sub(/(\.(js|rb|opal))*#{REGEXP_END}/, '.js')]
127
- return '' if asset.nil?
128
-
129
- opal_extnames = sprockets.engines.map do |ext, engine|
130
- ext if engine <= ::Opal::Processor
131
- end.compact
132
-
133
- module_name = -> asset { asset.logical_path.sub(/\.js#{REGEXP_END}/, '') }
134
- path_extnames = -> path { File.basename(path).scan(/\.[^.]+/) }
135
- mark_as_loaded = -> path { "Opal.mark_as_loaded(#{path.inspect});" }
136
- processed_by_opal = -> asset { (path_extnames[asset.pathname] & opal_extnames).any? }
137
-
138
- non_opal_assets = ([asset]+asset.dependencies)
139
- .select { |asset| not(processed_by_opal[asset]) }
140
- .map { |asset| module_name[asset] }
141
-
142
- mark_as_loaded = (['opal'] + non_opal_assets + stubbed_files.to_a)
143
- .map { |path| mark_as_loaded[path] }
144
-
145
- if processed_by_opal[asset]
146
- load_asset_code = "Opal.load(#{module_name[asset].inspect});"
147
- end
148
-
149
- <<-JS
150
- if (typeof(Opal) !== 'undefined') {
151
- #{mark_as_loaded.join("\n")}
152
- #{load_asset_code}
153
- }
154
- JS
130
+ ::Opal::Sprockets.load_asset(name, sprockets)
155
131
  end
156
132
 
157
133
  def self.stubbed_files
@@ -117,6 +117,7 @@ module Opal
117
117
  raise "index does not exist: #{@index_path}" unless File.exist?(@index_path)
118
118
  Tilt.new(@index_path).render(self)
119
119
  else
120
+ raise "Main asset path not configured (set 'main' within Opal::Server.new block)" if @server.main.nil?
120
121
  source
121
122
  end
122
123
  end
@@ -124,21 +125,7 @@ module Opal
124
125
  def javascript_include_tag name
125
126
  sprockets = @server.sprockets
126
127
  prefix = @server.prefix
127
- asset = sprockets[name]
128
- raise "Cannot find asset: #{name}" if asset.nil?
129
- scripts = []
130
-
131
- if @server.debug
132
- asset.to_a.map do |dependency|
133
- scripts << %{<script src="#{prefix}/#{dependency.logical_path}?body=1"></script>}
134
- end
135
- else
136
- scripts << %{<script src="#{prefix}/#{name}.js"></script>}
137
- end
138
-
139
- scripts << %{<script>#{Opal::Processor.load_asset_code(sprockets, name)}</script>}
140
-
141
- scripts.join "\n"
128
+ ::Opal::Sprockets.javascript_include_tag(name, sprockets: @server.sprockets, prefix: @server.prefix, debug: @server.debug)
142
129
  end
143
130
 
144
131
  def source
@@ -146,6 +133,7 @@ module Opal
146
133
  <!DOCTYPE html>
147
134
  <html>
148
135
  <head>
136
+ <meta charset="utf-8">
149
137
  <title>Opal Server</title>
150
138
  </head>
151
139
  <body>
data/lib/opal/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Opal
2
2
  # WHEN RELEASING:
3
- # Remember to update RUBY_ENGINE_VERSION in opal/corelib/variables.rb too!
4
- VERSION = '0.8.1'
3
+ # Remember to update RUBY_ENGINE_VERSION in opal/corelib/constants.rb too!
4
+ VERSION = '0.9.0.beta1'
5
5
  end
data/opal.gemspec CHANGED
@@ -9,16 +9,16 @@ Gem::Specification.new do |s|
9
9
  s.author = 'Adam Beynon'
10
10
  s.email = 'adam.beynon@gmail.com'
11
11
  s.homepage = 'http://opalrb.org'
12
- s.summary = 'Ruby runtime and core library for javascript'
13
- s.description = 'Ruby runtime and core library for javascript.'
12
+ s.summary = 'Ruby runtime and core library for JavaScript'
13
+ s.description = 'Ruby runtime and core library for JavaScript.'
14
14
  s.license = 'MIT'
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.require_paths = ['lib']
20
20
 
21
- required_ruby_version = '>= 1.9.3'
21
+ s.required_ruby_version = '>= 1.9.3'
22
22
 
23
23
  s.add_dependency 'sourcemap', '~> 0.1.0'
24
24
  s.add_dependency 'sprockets', '~> 3.1'
data/opal/README.md CHANGED
@@ -4,3 +4,12 @@ This is the Opal corelib implementation API documentation.
4
4
  The whole corelib is loaded upon `require 'opal'`.
5
5
 
6
6
  The `runtime.js` documentation is [available here](runtime.js.html)
7
+
8
+ # Cherry-picking
9
+
10
+ Note that `require 'opal'` will load all of the corelib, which is likely to
11
+ have a ton of stuff you don't need.
12
+
13
+ If you're concerned about runtime size, you can `require 'opal/base'` and
14
+ require anything you need, or `require 'opal/mini'` to have a working Ruby
15
+ without *useless* stuff.
@@ -1,6 +1,7 @@
1
1
  require 'corelib/enumerable'
2
+ require 'corelib/numeric'
2
3
 
3
- class Array
4
+ class Array < `Array`
4
5
  include Enumerable
5
6
 
6
7
  # Mark all javascript arrays as being valid ruby arrays
@@ -10,18 +11,17 @@ class Array
10
11
  objects
11
12
  end
12
13
 
13
- def initialize(*args)
14
- self.class.new(*args)
15
- end
16
-
17
- def self.new(size = nil, obj = nil, &block)
14
+ def initialize(size = nil, obj = nil, &block)
18
15
  if `arguments.length > 2`
19
16
  raise ArgumentError, "wrong number of arguments (#{`arguments.length`} for 0..2)"
20
17
  end
21
18
 
22
- if `arguments.length === 0`
23
- return []
24
- end
19
+ %x{
20
+ if (arguments.length === 0) {
21
+ self.splice(0, self.length);
22
+ return self;
23
+ }
24
+ }
25
25
 
26
26
  if `arguments.length === 1`
27
27
  if Array === size
@@ -38,29 +38,34 @@ class Array
38
38
  end
39
39
 
40
40
  %x{
41
- var result = [];
41
+ self.splice(0, self.length);
42
+ var i, value;
42
43
 
43
44
  if (block === nil) {
44
- for (var i = 0; i < size; i++) {
45
- result.push(obj);
45
+ for (i = 0; i < size; i++) {
46
+ self.push(obj);
46
47
  }
47
48
  }
48
49
  else {
49
- for (var i = 0, value; i < size; i++) {
50
+ for (i = 0, value; i < size; i++) {
50
51
  value = block(i);
51
52
 
52
53
  if (value === $breaker) {
53
54
  return $breaker.$v;
54
55
  }
55
56
 
56
- result[i] = value;
57
+ self[i] = value;
57
58
  }
58
59
  }
59
60
 
60
- return result;
61
+ return self;
61
62
  }
62
63
  end
63
64
 
65
+ def self.new(*args, &block)
66
+ [].initialize(*args, &block)
67
+ end
68
+
64
69
  def self.try_convert(obj)
65
70
  Opal.coerce_to? obj, Array, :to_ary
66
71
  end
@@ -73,21 +78,16 @@ class Array
73
78
  end
74
79
 
75
80
  %x{
76
- var result = [],
77
- seen = {};
78
-
79
- for (var i = 0, length = self.length; i < length; i++) {
80
- var item = self[i];
81
+ var result = [], hash = #{{}}, i, length, item;
81
82
 
82
- if (!seen[item]) {
83
- for (var j = 0, length2 = other.length; j < length2; j++) {
84
- var item2 = other[j];
83
+ for (i = 0, length = other.length; i < length; i++) {
84
+ Opal.hash_put(hash, other[i], true);
85
+ }
85
86
 
86
- if (!seen[item2] && #{`item` == `item2`}) {
87
- seen[item] = true;
88
- result.push(item);
89
- }
90
- }
87
+ for (i = 0, length = self.length; i < length; i++) {
88
+ item = self[i];
89
+ if (Opal.hash_delete(hash, item) !== undefined) {
90
+ result.push(item);
91
91
  }
92
92
  }
93
93
 
@@ -103,32 +103,22 @@ class Array
103
103
  end
104
104
 
105
105
  %x{
106
- var result = [],
107
- seen = {};
106
+ var hash = #{{}}, i, length, item;
108
107
 
109
- for (var i = 0, length = self.length; i < length; i++) {
110
- var item = self[i];
111
-
112
- if (!seen[item]) {
113
- seen[item] = true;
114
- result.push(item);
115
- }
108
+ for (i = 0, length = self.length; i < length; i++) {
109
+ Opal.hash_put(hash, self[i], true);
116
110
  }
117
111
 
118
- for (var i = 0, length = other.length; i < length; i++) {
119
- var item = other[i];
120
-
121
- if (!seen[item]) {
122
- seen[item] = true;
123
- result.push(item);
124
- }
112
+ for (i = 0, length = other.length; i < length; i++) {
113
+ Opal.hash_put(hash, other[i], true);
125
114
  }
126
- return result;
115
+
116
+ return hash.$keys();
127
117
  }
128
118
  end
129
119
 
130
120
  def *(other)
131
- return `self.join(#{other.to_str})` if other.respond_to? :to_str
121
+ return join(other.to_str) if other.respond_to? :to_str
132
122
 
133
123
  unless other.respond_to? :to_int
134
124
  raise TypeError, "no implicit conversion of #{other.class} into Integer"
@@ -172,17 +162,15 @@ class Array
172
162
  return clone if `other.length === 0`
173
163
 
174
164
  %x{
175
- var seen = {},
176
- result = [];
165
+ var result = [], hash = #{{}}, i, length, item;
177
166
 
178
- for (var i = 0, length = other.length; i < length; i++) {
179
- seen[other[i]] = true;
167
+ for (i = 0, length = other.length; i < length; i++) {
168
+ Opal.hash_put(hash, other[i], true);
180
169
  }
181
170
 
182
- for (var i = 0, length = self.length; i < length; i++) {
183
- var item = self[i];
184
-
185
- if (!seen[item]) {
171
+ for (i = 0, length = self.length; i < length; i++) {
172
+ item = self[i];
173
+ if (Opal.hash_get(hash, item) === undefined) {
186
174
  result.push(item);
187
175
  }
188
176
  }
@@ -211,11 +199,9 @@ class Array
211
199
  return 0;
212
200
  }
213
201
 
214
- if (self.length != other.length) {
215
- return (self.length > other.length) ? 1 : -1;
216
- }
202
+ var count = Math.min(self.length, other.length);
217
203
 
218
- for (var i = 0, length = self.length; i < length; i++) {
204
+ for (var i = 0; i < count; i++) {
219
205
  var tmp = #{`self[i]` <=> `other[i]`};
220
206
 
221
207
  if (tmp !== 0) {
@@ -223,7 +209,7 @@ class Array
223
209
  }
224
210
  }
225
211
 
226
- return 0;
212
+ return #{`self.length` <=> `other.length`};
227
213
  }
228
214
  end
229
215
 
@@ -277,12 +263,14 @@ class Array
277
263
  end
278
264
 
279
265
  def [](index, length = undefined)
280
- if Range === index
281
- %x{
282
- var size = self.length,
283
- exclude = index.exclude,
284
- from = #{Opal.coerce_to `index.begin`, Integer, :to_int},
285
- to = #{Opal.coerce_to `index.end`, Integer, :to_int};
266
+ %x{
267
+ var size = self.length,
268
+ exclude, from, to;
269
+
270
+ if (index.$$is_range) {
271
+ exclude = index.exclude;
272
+ from = #{Opal.coerce_to `index.begin`, Integer, :to_int};
273
+ to = #{Opal.coerce_to `index.end`, Integer, :to_int};
286
274
 
287
275
  if (from < 0) {
288
276
  from += size;
@@ -310,11 +298,8 @@ class Array
310
298
 
311
299
  return self.slice(from, to);
312
300
  }
313
- else
314
- index = Opal.coerce_to index, Integer, :to_int
315
-
316
- %x{
317
- var size = self.length;
301
+ else {
302
+ index = #{Opal.coerce_to(index, Integer, :to_int)};
318
303
 
319
304
  if (index < 0) {
320
305
  index += size;
@@ -341,10 +326,14 @@ class Array
341
326
  return self.slice(index, index + length);
342
327
  }
343
328
  }
344
- end
329
+ }
345
330
  end
346
331
 
347
332
  def []=(index, value, extra = undefined)
333
+ %x{
334
+ var i, size = self.length;
335
+ }
336
+
348
337
  if Range === index
349
338
  if Array === value
350
339
  data = value.to_a
@@ -355,8 +344,7 @@ class Array
355
344
  end
356
345
 
357
346
  %x{
358
- var size = self.length,
359
- exclude = index.exclude,
347
+ var exclude = index.exclude,
360
348
  from = #{Opal.coerce_to `index.begin`, Integer, :to_int},
361
349
  to = #{Opal.coerce_to `index.end`, Integer, :to_int};
362
350
 
@@ -377,7 +365,7 @@ class Array
377
365
  }
378
366
 
379
367
  if (from > size) {
380
- for (var i = size; i < from; i++) {
368
+ for (i = size; i < from; i++) {
381
369
  self[i] = nil;
382
370
  }
383
371
  }
@@ -408,10 +396,10 @@ class Array
408
396
  end
409
397
 
410
398
  %x{
411
- var size = self.length,
412
- index = #{Opal.coerce_to index, Integer, :to_int},
413
- length = #{Opal.coerce_to length, Integer, :to_int},
414
- old;
399
+ var old;
400
+
401
+ index = #{Opal.coerce_to index, Integer, :to_int};
402
+ length = #{Opal.coerce_to length, Integer, :to_int};
415
403
 
416
404
  if (index < 0) {
417
405
  old = index;
@@ -427,7 +415,7 @@ class Array
427
415
  }
428
416
 
429
417
  if (index > size) {
430
- for (var i = size; i < index; i++) {
418
+ for (i = size; i < index; i++) {
431
419
  self[i] = nil;
432
420
  }
433
421
  }
@@ -515,15 +503,24 @@ class Array
515
503
  end
516
504
 
517
505
  def cycle(n = nil, &block)
506
+ return enum_for(:cycle, n) {
507
+ if n == nil
508
+ Float::INFINITY
509
+ else
510
+ n = Opal.coerce_to!(n, Integer, :to_int)
511
+ n > 0 ? self.enumerator_size * n : 0
512
+ end
513
+ } unless block_given?
514
+
518
515
  return if empty? || n == 0
519
516
 
520
- return enum_for :cycle, n unless block
517
+ %x{
518
+ var i, length, value;
521
519
 
522
- if n.nil?
523
- %x{
520
+ if (n === nil) {
524
521
  while (true) {
525
- for (var i = 0, length = self.length; i < length; i++) {
526
- var value = Opal.yield1(block, self[i]);
522
+ for (i = 0, length = self.length; i < length; i++) {
523
+ value = Opal.yield1(block, self[i]);
527
524
 
528
525
  if (value === $breaker) {
529
526
  return $breaker.$v;
@@ -531,17 +528,15 @@ class Array
531
528
  }
532
529
  }
533
530
  }
534
- else
535
- n = Opal.coerce_to! n, Integer, :to_int
536
-
537
- %x{
531
+ else {
532
+ n = #{Opal.coerce_to!(n, Integer, :to_int)};
538
533
  if (n <= 0) {
539
534
  return self;
540
535
  }
541
536
 
542
537
  while (n > 0) {
543
- for (var i = 0, length = self.length; i < length; i++) {
544
- var value = Opal.yield1(block, self[i]);
538
+ for (i = 0, length = self.length; i < length; i++) {
539
+ value = Opal.yield1(block, self[i]);
545
540
 
546
541
  if (value === $breaker) {
547
542
  return $breaker.$v;
@@ -551,7 +546,7 @@ class Array
551
546
  n--;
552
547
  }
553
548
  }
554
- end
549
+ }
555
550
 
556
551
  self
557
552
  end
@@ -564,6 +559,7 @@ class Array
564
559
 
565
560
  def clone
566
561
  copy = []
562
+ copy.copy_singleton_methods(self)
567
563
  copy.initialize_clone(self)
568
564
  copy
569
565
  end
@@ -579,7 +575,7 @@ class Array
579
575
  end
580
576
 
581
577
  def collect(&block)
582
- return enum_for :collect unless block_given?
578
+ return enum_for(:collect){self.size} unless block_given?
583
579
 
584
580
  %x{
585
581
  var result = [];
@@ -599,7 +595,7 @@ class Array
599
595
  end
600
596
 
601
597
  def collect!(&block)
602
- return enum_for :collect! unless block_given?
598
+ return enum_for(:collect!){self.size} unless block_given?
603
599
 
604
600
  %x{
605
601
  for (var i = 0, length = self.length; i < length; i++) {
@@ -616,9 +612,23 @@ class Array
616
612
  self
617
613
  end
618
614
 
615
+ %x{
616
+ function binomial_coefficient(n, k) {
617
+ if (n === k || k === 0) {
618
+ return 1;
619
+ }
620
+
621
+ if (k > 0 && n > k) {
622
+ return binomial_coefficient(n - 1, k - 1) + binomial_coefficient(n - 1, k);
623
+ }
624
+
625
+ return 0;
626
+ }
627
+ }
628
+
619
629
  def combination(n)
620
630
  num = Opal.coerce_to! n, Integer, :to_int
621
- return enum_for :combination, num unless block_given?
631
+ return enum_for(:combination, num){ `binomial_coefficient(#{self}.length, num)` } unless block_given?
622
632
 
623
633
  %x{
624
634
  var i, length, stack, chosen, lev, done, next;
@@ -755,7 +765,7 @@ class Array
755
765
  end
756
766
 
757
767
  def delete_if(&block)
758
- return enum_for :delete_if unless block_given?
768
+ return enum_for(:delete_if){self.size} unless block_given?
759
769
 
760
770
  %x{
761
771
  for (var i = 0, length = self.length, value; i < length; i++) {
@@ -785,10 +795,8 @@ class Array
785
795
  }
786
796
  end
787
797
 
788
- alias dup clone
789
-
790
798
  def each(&block)
791
- return enum_for :each unless block_given?
799
+ return enum_for(:each){self.size} unless block_given?
792
800
 
793
801
  %x{
794
802
  for (var i = 0, length = self.length; i < length; i++) {
@@ -804,7 +812,7 @@ class Array
804
812
  end
805
813
 
806
814
  def each_index(&block)
807
- return enum_for :each_index unless block_given?
815
+ return enum_for(:each_index){self.size} unless block_given?
808
816
 
809
817
  %x{
810
818
  for (var i = 0, length = self.length; i < length; i++) {
@@ -900,6 +908,10 @@ class Array
900
908
  end
901
909
 
902
910
  def fill(*args, &block)
911
+ %x{
912
+ var i, length, value;
913
+ }
914
+
903
915
  if block
904
916
  if `args.length > 2`
905
917
  raise ArgumentError, "wrong number of arguments (#{args.length} for 0..2)"
@@ -949,7 +961,7 @@ class Array
949
961
 
950
962
  if `left > #@length`
951
963
  %x{
952
- for (var i = #@length; i < right; i++) {
964
+ for (i = #@length; i < right; i++) {
953
965
  self[i] = nil;
954
966
  }
955
967
  }
@@ -961,8 +973,8 @@ class Array
961
973
 
962
974
  if block
963
975
  %x{
964
- for (var length = #@length; left < right; left++) {
965
- var value = block(left);
976
+ for (length = #@length; left < right; left++) {
977
+ value = block(left);
966
978
 
967
979
  if (value === $breaker) {
968
980
  return $breaker.$v;
@@ -973,7 +985,7 @@ class Array
973
985
  }
974
986
  else
975
987
  %x{
976
- for (var length = #@length; left < right; left++) {
988
+ for (length = #@length; left < right; left++) {
977
989
  self[left] = #{obj};
978
990
  }
979
991
  }
@@ -1000,14 +1012,13 @@ class Array
1000
1012
 
1001
1013
  def flatten(level = undefined)
1002
1014
  %x{
1003
- var object_id = #{`self`.object_id};
1004
-
1005
1015
  function _flatten(array, level) {
1006
- var array = #{`array`.to_a},
1007
- result = [],
1016
+ var result = [],
1008
1017
  i, length,
1009
1018
  item, ary;
1010
1019
 
1020
+ array = #{`array`.to_a};
1021
+
1011
1022
  for (i = 0, length = array.length; i < length; i++) {
1012
1023
  item = array[i];
1013
1024
 
@@ -1027,7 +1038,7 @@ class Array
1027
1038
  #{raise TypeError};
1028
1039
  }
1029
1040
 
1030
- if (object_id === #{`ary`.object_id}) {
1041
+ if (ary === self) {
1031
1042
  #{raise ArgumentError};
1032
1043
  }
1033
1044
 
@@ -1077,17 +1088,42 @@ class Array
1077
1088
 
1078
1089
  def hash
1079
1090
  %x{
1080
- var hash = ['A'],
1081
- item;
1082
- for (var i = 0, length = self.length; i < length; i++) {
1083
- item = self[i];
1084
- if (item.$$is_array && #{`self`.eql?(`item`)}) {
1085
- hash.push('self');
1086
- } else {
1087
- hash.push(item.$hash());
1091
+ var top = (Opal.hash_ids == undefined),
1092
+ result = ['A'],
1093
+ hash_id = self.$object_id(),
1094
+ item, i, key;
1095
+
1096
+ try {
1097
+ if (top) {
1098
+ Opal.hash_ids = {};
1099
+ }
1100
+
1101
+ if (Opal.hash_ids.hasOwnProperty(hash_id)) {
1102
+ return 'self';
1103
+ }
1104
+
1105
+ for (key in Opal.hash_ids) {
1106
+ if (Opal.hash_ids.hasOwnProperty(key)) {
1107
+ item = Opal.hash_ids[key];
1108
+ if (#{eql?(`item`)}) {
1109
+ return 'self';
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ Opal.hash_ids[hash_id] = self;
1115
+
1116
+ for (i = 0; i < self.length; i++) {
1117
+ item = self[i];
1118
+ result.push(item.$hash());
1119
+ }
1120
+
1121
+ return result.join(',');
1122
+ } finally {
1123
+ if (top) {
1124
+ delete Opal.hash_ids;
1088
1125
  }
1089
1126
  }
1090
- return hash.join(',');
1091
1127
  }
1092
1128
  end
1093
1129
 
@@ -1105,15 +1141,17 @@ class Array
1105
1141
 
1106
1142
  def index(object=undefined, &block)
1107
1143
  %x{
1144
+ var i, length, value;
1145
+
1108
1146
  if (object != null) {
1109
- for (var i = 0, length = self.length; i < length; i++) {
1147
+ for (i = 0, length = self.length; i < length; i++) {
1110
1148
  if (#{`self[i]` == object}) {
1111
1149
  return i;
1112
1150
  }
1113
1151
  }
1114
1152
  }
1115
1153
  else if (block !== nil) {
1116
- for (var i = 0, length = self.length, value; i < length; i++) {
1154
+ for (i = 0, length = self.length; i < length; i++) {
1117
1155
  if ((value = block(self[i])) === $breaker) {
1118
1156
  return $breaker.$v;
1119
1157
  }
@@ -1185,13 +1223,13 @@ class Array
1185
1223
 
1186
1224
  %x{
1187
1225
  var result = [];
1188
- var object_id = #{`self`.object_id};
1226
+ var i, length, item, tmp;
1189
1227
 
1190
- for (var i = 0, length = self.length; i < length; i++) {
1191
- var item = self[i];
1228
+ for (i = 0, length = self.length; i < length; i++) {
1229
+ item = self[i];
1192
1230
 
1193
1231
  if (#{Opal.respond_to? `item`, :to_str}) {
1194
- var tmp = #{`item`.to_str};
1232
+ tmp = #{`item`.to_str};
1195
1233
 
1196
1234
  if (tmp !== nil) {
1197
1235
  result.push(#{`tmp`.to_s});
@@ -1201,9 +1239,9 @@ class Array
1201
1239
  }
1202
1240
 
1203
1241
  if (#{Opal.respond_to? `item`, :to_ary}) {
1204
- var tmp = #{`item`.to_ary};
1242
+ tmp = #{`item`.to_ary};
1205
1243
 
1206
- if (object_id === #{`tmp`.object_id}) {
1244
+ if (tmp === self) {
1207
1245
  #{raise ArgumentError};
1208
1246
  }
1209
1247
 
@@ -1215,7 +1253,7 @@ class Array
1215
1253
  }
1216
1254
 
1217
1255
  if (#{Opal.respond_to? `item`, :to_s}) {
1218
- var tmp = #{`item`.to_s};
1256
+ tmp = #{`item`.to_s};
1219
1257
 
1220
1258
  if (tmp !== nil) {
1221
1259
  result.push(tmp);
@@ -1237,7 +1275,7 @@ class Array
1237
1275
  end
1238
1276
 
1239
1277
  def keep_if(&block)
1240
- return enum_for :keep_if unless block_given?
1278
+ return enum_for(:keep_if){self.size} unless block_given?
1241
1279
 
1242
1280
  %x{
1243
1281
  for (var i = 0, length = self.length, value; i < length; i++) {
@@ -1284,7 +1322,73 @@ class Array
1284
1322
  alias map collect
1285
1323
 
1286
1324
  alias map! collect!
1325
+
1326
+ def permutation(num = undefined, &block)
1327
+ return enum_for(:permutation, num){self.size} unless block_given?
1328
+
1329
+ %x{
1330
+ var permute, offensive, output;
1331
+
1332
+ if (num === undefined) {
1333
+ num = self.length;
1334
+ }
1335
+ else {
1336
+ num = #{ Opal.coerce_to num, Integer, :to_int }
1337
+ }
1338
+
1339
+ if (num < 0 || self.length < num) {
1340
+ // no permutations, yield nothing
1341
+ }
1342
+ else if (num === 0) {
1343
+ // exactly one permutation: the zero-length array
1344
+ #{ yield [] }
1345
+ }
1346
+ else if (num === 1) {
1347
+ // this is a special, easy case
1348
+ for (var i = 0; i < self.length; i++) {
1349
+ #{ yield `[self[i]]` }
1350
+ }
1351
+ }
1352
+ else {
1353
+ // this is the general case
1354
+ #{ perm = Array.new(num) }
1355
+ #{ used = Array.new(`self.length`, false) }
1356
+
1357
+ permute = function(num, perm, index, used, blk) {
1358
+ self = this;
1359
+ for(var i = 0; i < self.length; i++){
1360
+ if(#{ !used[`i`] }) {
1361
+ perm[index] = i;
1362
+ if(index < num - 1) {
1363
+ used[i] = true;
1364
+ permute.call(self, num, perm, index + 1, used, blk);
1365
+ used[i] = false;
1366
+ }
1367
+ else {
1368
+ output = [];
1369
+ for (var j = 0; j < perm.length; j++) {
1370
+ output.push(self[perm[j]]);
1371
+ }
1372
+ Opal.yield1(blk, output);
1373
+ }
1374
+ }
1375
+ }
1376
+ }
1377
+
1378
+ if (#{block_given?}) {
1379
+ // offensive (both definitions) copy.
1380
+ offensive = self.slice();
1381
+ permute.call(offensive, num, perm, 0, used, block);
1382
+ }
1383
+ else {
1384
+ permute.call(self, num, perm, 0, used, block);
1385
+ }
1386
+ }
1387
+ }
1287
1388
 
1389
+ self
1390
+ end
1391
+
1288
1392
  def pop(count = undefined)
1289
1393
  if `count === undefined`
1290
1394
  return if `self.length === 0`
@@ -1383,7 +1487,7 @@ class Array
1383
1487
  end
1384
1488
 
1385
1489
  def reject(&block)
1386
- return enum_for :reject unless block_given?
1490
+ return enum_for(:reject){self.size} unless block_given?
1387
1491
 
1388
1492
  %x{
1389
1493
  var result = [];
@@ -1402,7 +1506,7 @@ class Array
1402
1506
  end
1403
1507
 
1404
1508
  def reject!(&block)
1405
- return enum_for :reject! unless block_given?
1509
+ return enum_for(:reject!){self.size} unless block_given?
1406
1510
 
1407
1511
  original = length
1408
1512
  delete_if(&block)
@@ -1436,7 +1540,7 @@ class Array
1436
1540
  end
1437
1541
 
1438
1542
  def reverse_each(&block)
1439
- return enum_for :reverse_each unless block_given?
1543
+ return enum_for(:reverse_each){self.size} unless block_given?
1440
1544
 
1441
1545
  reverse.each &block
1442
1546
  self
@@ -1444,19 +1548,26 @@ class Array
1444
1548
 
1445
1549
  def rindex(object = undefined, &block)
1446
1550
  %x{
1551
+ var i, value;
1552
+
1447
1553
  if (object != null) {
1448
- for (var i = self.length - 1; i >= 0; i--) {
1554
+ for (i = self.length - 1; i >= 0; i--) {
1555
+ if (i >= self.length) {
1556
+ break;
1557
+ }
1449
1558
  if (#{`self[i]` == `object`}) {
1450
1559
  return i;
1451
1560
  }
1452
1561
  }
1453
1562
  }
1454
1563
  else if (block !== nil) {
1455
- for (var i = self.length - 1, value; i >= 0; i--) {
1564
+ for (i = self.length - 1; i >= 0; i--) {
1565
+ if (i >= self.length) {
1566
+ break;
1567
+ }
1456
1568
  if ((value = block(self[i])) === $breaker) {
1457
1569
  return $breaker.$v;
1458
1570
  }
1459
-
1460
1571
  if (value !== false && value !== nil) {
1461
1572
  return i;
1462
1573
  }
@@ -1492,8 +1603,6 @@ class Array
1492
1603
  end
1493
1604
 
1494
1605
  def rotate!(cnt=1)
1495
- raise RuntimeError, "can't modify frozen Array" if frozen?
1496
-
1497
1606
  %x{
1498
1607
  if (self.length === 0 || self.length === 1) {
1499
1608
  return self;
@@ -1504,21 +1613,133 @@ class Array
1504
1613
  replace ary
1505
1614
  end
1506
1615
 
1507
- def sample(n = nil)
1508
- return nil if !n && empty?
1509
- return [] if n && empty?
1616
+ class SampleRandom
1617
+ def initialize(rng)
1618
+ @rng = rng
1619
+ end
1510
1620
 
1511
- if n
1512
- (1 .. n).map {
1513
- self[rand(length)]
1514
- }
1515
- else
1516
- self[rand(length)]
1621
+ def rand(size)
1622
+ random = Opal.coerce_to @rng.rand(size), Integer, :to_int
1623
+ raise RangeError, "random value must be >= 0" if `random < 0`
1624
+ raise RangeError, "random value must be less than Array size" unless `random < size`
1625
+
1626
+ random
1517
1627
  end
1518
1628
  end
1519
1629
 
1630
+ def sample(count = undefined, options = undefined)
1631
+ return at Kernel.rand(`self.length`) if `count === undefined`
1632
+
1633
+ if `options === undefined`
1634
+ if (o = Opal.coerce_to? count, Hash, :to_hash)
1635
+ options = o
1636
+ count = nil
1637
+ else
1638
+ options = nil
1639
+ count = Opal.coerce_to count, Integer, :to_int
1640
+ end
1641
+ else
1642
+ count = Opal.coerce_to count, Integer, :to_int
1643
+ options = Opal.coerce_to options, Hash, :to_hash
1644
+ end
1645
+
1646
+ if count and `count < 0`
1647
+ raise ArgumentError, "count must be greater than 0"
1648
+ end
1649
+
1650
+ rng = options[:random] if options
1651
+ if rng and rng.respond_to? :rand
1652
+ rng = SampleRandom.new rng
1653
+ else
1654
+ rng = Kernel
1655
+ end
1656
+
1657
+ return `self[#{rng.rand(`self.length`)}]` unless count
1658
+
1659
+ %x{
1660
+
1661
+ var abandon, spin, result, i, j, k, targetIndex, oldValue;
1662
+
1663
+ if (count > self.length) {
1664
+ count = self.length;
1665
+ }
1666
+
1667
+ switch (count) {
1668
+ case 0:
1669
+ return [];
1670
+ break;
1671
+ case 1:
1672
+ return [self[#{rng.rand(`self.length`)}]];
1673
+ break;
1674
+ case 2:
1675
+ i = #{rng.rand(`self.length`)};
1676
+ j = #{rng.rand(`self.length`)};
1677
+ if (i === j) {
1678
+ j = i === 0 ? i + 1 : i - 1;
1679
+ }
1680
+ return [self[i], self[j]];
1681
+ break;
1682
+ default:
1683
+ if (self.length / count > 3) {
1684
+ abandon = false;
1685
+ spin = 0;
1686
+
1687
+ result = #{ Array.new(count) };
1688
+ i = 1;
1689
+
1690
+ result[0] = #{rng.rand(`self.length`)};
1691
+ while (i < count) {
1692
+ k = #{rng.rand(`self.length`)};
1693
+ j = 0;
1694
+
1695
+ while (j < i) {
1696
+ while (k === result[j]) {
1697
+ spin++;
1698
+ if (spin > 100) {
1699
+ abandon = true;
1700
+ break;
1701
+ }
1702
+ k = #{rng.rand(`self.length`)};
1703
+ }
1704
+ if (abandon) { break; }
1705
+
1706
+ j++;
1707
+ }
1708
+
1709
+ if (abandon) { break; }
1710
+
1711
+ result[i] = k;
1712
+
1713
+ i++;
1714
+ }
1715
+
1716
+ if (!abandon) {
1717
+ i = 0;
1718
+ while (i < count) {
1719
+ result[i] = self[result[i]];
1720
+ i++;
1721
+ }
1722
+
1723
+ return result;
1724
+ }
1725
+ }
1726
+
1727
+ result = self.slice();
1728
+
1729
+ for (var c = 0; c < count; c++) {
1730
+ targetIndex = #{rng.rand(`self.length`)};
1731
+ oldValue = result[c];
1732
+ result[c] = result[targetIndex];
1733
+ result[targetIndex] = oldValue;
1734
+ }
1735
+
1736
+ return count === self.length ? result : #{`result`[0, count]};
1737
+ }
1738
+ }
1739
+ end
1740
+
1520
1741
  def select(&block)
1521
- return enum_for :select unless block_given?
1742
+ return enum_for(:select){self.size} unless block_given?
1522
1743
 
1523
1744
  %x{
1524
1745
  var result = [];
@@ -1540,7 +1761,7 @@ class Array
1540
1761
  end
1541
1762
 
1542
1763
  def select!(&block)
1543
- return enum_for :select! unless block_given?
1764
+ return enum_for(:select!){self.size} unless block_given?
1544
1765
 
1545
1766
  %x{
1546
1767
  var original = self.length;
@@ -1568,22 +1789,49 @@ class Array
1568
1789
 
1569
1790
  alias size length
1570
1791
 
1571
- def shuffle
1572
- clone.shuffle!
1792
+ def shuffle(rng = undefined)
1793
+ dup.shuffle!(rng)
1573
1794
  end
1574
1795
 
1575
- def shuffle!
1796
+ def shuffle!(rng = undefined)
1576
1797
  %x{
1577
- for (var i = self.length - 1; i > 0; i--) {
1578
- var tmp = self[i],
1579
- j = Math.floor(Math.random() * (i + 1));
1798
+ var randgen, i = self.length, j, tmp;
1799
+
1800
+ if (rng !== undefined) {
1801
+ rng = #{Opal.coerce_to?(rng, Hash, :to_hash)};
1802
+
1803
+ if (rng !== nil) {
1804
+ rng = #{rng[:random]};
1805
+
1806
+ if (rng !== nil && #{rng.respond_to?(:rand)}) {
1807
+ randgen = rng;
1808
+ }
1809
+ }
1810
+ }
1811
+
1812
+ while (i) {
1813
+ if (randgen) {
1814
+ j = randgen.$rand(i).$to_int();
1815
+
1816
+ if (j < 0) {
1817
+ #{raise RangeError, "random number too small #{`j`}"}
1818
+ }
1819
+
1820
+ if (j >= i) {
1821
+ #{raise RangeError, "random number too big #{`j`}"}
1822
+ }
1823
+ }
1824
+ else {
1825
+ j = Math.floor(Math.random() * i);
1826
+ }
1580
1827
 
1828
+ tmp = self[--i];
1581
1829
  self[i] = self[j];
1582
1830
  self[j] = tmp;
1583
1831
  }
1584
- }
1585
1832
 
1586
- self
1833
+ return self;
1834
+ }
1587
1835
  end
1588
1836
 
1589
1837
  alias slice []
@@ -1610,7 +1858,7 @@ class Array
1610
1858
  return self unless `self.length > 1`
1611
1859
 
1612
1860
  %x{
1613
- if (!#{block_given?}) {
1861
+ if (block === nil) {
1614
1862
  block = function(a, b) {
1615
1863
  return #{`a` <=> `b`};
1616
1864
  };
@@ -1713,7 +1961,7 @@ class Array
1713
1961
  }
1714
1962
  key = ary[0];
1715
1963
  val = ary[1];
1716
- hash.$store(key, val);
1964
+ Opal.hash_put(hash, key, val);
1717
1965
  }
1718
1966
 
1719
1967
  return hash;
@@ -1750,47 +1998,51 @@ class Array
1750
1998
  result
1751
1999
  end
1752
2000
 
1753
- def uniq
2001
+ def uniq(&block)
1754
2002
  %x{
1755
- var result = [],
1756
- seen = {};
2003
+ var hash = #{{}}, i, length, item, key;
1757
2004
 
1758
- for (var i = 0, length = self.length, item, hash; i < length; i++) {
1759
- item = self[i];
1760
- hash = item;
1761
-
1762
- if (!seen[hash]) {
1763
- seen[hash] = true;
1764
-
1765
- result.push(item);
2005
+ if (block === nil) {
2006
+ for (i = 0, length = self.length; i < length; i++) {
2007
+ item = self[i];
2008
+ if (Opal.hash_get(hash, item) === undefined) {
2009
+ Opal.hash_put(hash, item, item);
2010
+ }
2011
+ }
2012
+ }
2013
+ else {
2014
+ for (i = 0, length = self.length; i < length; i++) {
2015
+ item = self[i];
2016
+ key = Opal.yield1(block, item);
2017
+ if (Opal.hash_get(hash, key) === undefined) {
2018
+ Opal.hash_put(hash, key, item);
2019
+ }
1766
2020
  }
1767
2021
  }
1768
2022
 
1769
- return result;
2023
+ return hash.$values();
1770
2024
  }
1771
2025
  end
1772
2026
 
1773
- def uniq!
2027
+ def uniq!(&block)
1774
2028
  %x{
1775
- var original = self.length,
1776
- seen = {};
2029
+ var original_length = self.length, hash = #{{}}, i, length, item, key;
1777
2030
 
1778
- for (var i = 0, length = original, item, hash; i < length; i++) {
2031
+ for (i = 0, length = original_length; i < length; i++) {
1779
2032
  item = self[i];
1780
- hash = item;
2033
+ key = (block === nil ? item : Opal.yield1(block, item));
1781
2034
 
1782
- if (!seen[hash]) {
1783
- seen[hash] = true;
2035
+ if (Opal.hash_get(hash, key) === undefined) {
2036
+ Opal.hash_put(hash, key, item);
2037
+ continue;
1784
2038
  }
1785
- else {
1786
- self.splice(i, 1);
1787
2039
 
1788
- length--;
1789
- i--;
1790
- }
2040
+ self.splice(i, 1);
2041
+ length--;
2042
+ i--;
1791
2043
  }
1792
2044
 
1793
- return self.length === original ? nil : self;
2045
+ return self.length === original_length ? nil : self;
1794
2046
  }
1795
2047
  end
1796
2048