opal 0.8.1 → 0.9.0.beta1

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