opal 0.9.4 → 0.10.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.gitignore +2 -3
  4. data/.gitmodules +5 -2
  5. data/.jshintrc +1 -8
  6. data/.rspec +1 -1
  7. data/.travis.yml +15 -23
  8. data/CHANGELOG.md +511 -326
  9. data/CODE_OF_CONDUCT.md +13 -15
  10. data/CONTRIBUTING.md +26 -216
  11. data/Gemfile +20 -12
  12. data/Guardfile +2 -2
  13. data/HACKING.md +230 -0
  14. data/README.md +6 -7
  15. data/bin/opal-mspec +1 -1
  16. data/config.ru +2 -2
  17. data/docs/faq.md +1 -1
  18. data/docs/source_maps.md +1 -1
  19. data/lib/opal.rb +1 -0
  20. data/lib/opal/builder.rb +1 -1
  21. data/lib/opal/cli.rb +30 -28
  22. data/lib/opal/cli_options.rb +3 -0
  23. data/lib/opal/cli_runners.rb +14 -1
  24. data/lib/opal/cli_runners/{apple_script.rb → applescript.rb} +3 -3
  25. data/lib/opal/cli_runners/nashorn.rb +2 -2
  26. data/lib/opal/cli_runners/nodejs.rb +2 -2
  27. data/lib/opal/cli_runners/phantom.js +24 -0
  28. data/lib/opal/cli_runners/phantomjs.rb +10 -10
  29. data/lib/opal/cli_runners/server.rb +3 -3
  30. data/lib/opal/compiler.rb +43 -4
  31. data/lib/opal/config.rb +3 -1
  32. data/lib/opal/errors.rb +13 -0
  33. data/lib/opal/fragment.rb +0 -13
  34. data/lib/opal/nodes.rb +10 -0
  35. data/lib/opal/nodes/args/initialize_kwargs.rb +28 -0
  36. data/lib/opal/nodes/args/kwarg.rb +29 -0
  37. data/lib/opal/nodes/args/kwoptarg.rb +29 -0
  38. data/lib/opal/nodes/args/kwrestarg.rb +39 -0
  39. data/lib/opal/nodes/args/mlhsarg.rb +79 -0
  40. data/lib/opal/nodes/args/normarg.rb +26 -0
  41. data/lib/opal/nodes/args/optarg.rb +27 -0
  42. data/lib/opal/nodes/args/post_args.rb +200 -0
  43. data/lib/opal/nodes/args/post_kwargs.rb +31 -0
  44. data/lib/opal/nodes/args/restarg.rb +33 -0
  45. data/lib/opal/nodes/base.rb +12 -0
  46. data/lib/opal/nodes/call.rb +92 -33
  47. data/lib/opal/nodes/def.rb +26 -169
  48. data/lib/opal/nodes/hash.rb +10 -4
  49. data/lib/opal/nodes/helpers.rb +6 -3
  50. data/lib/opal/nodes/inline_args.rb +61 -0
  51. data/lib/opal/nodes/iter.rb +73 -82
  52. data/lib/opal/nodes/logic.rb +12 -2
  53. data/lib/opal/nodes/masgn.rb +1 -2
  54. data/lib/opal/nodes/node_with_args.rb +141 -0
  55. data/lib/opal/nodes/rescue.rb +121 -43
  56. data/lib/opal/nodes/scope.rb +24 -5
  57. data/lib/opal/nodes/super.rb +122 -54
  58. data/lib/opal/nodes/top.rb +0 -12
  59. data/lib/opal/nodes/yield.rb +2 -13
  60. data/lib/opal/parser.rb +67 -39
  61. data/lib/opal/parser/grammar.rb +3319 -2961
  62. data/lib/opal/parser/grammar.y +234 -46
  63. data/lib/opal/parser/lexer.rb +105 -17
  64. data/lib/opal/parser/sexp.rb +4 -0
  65. data/lib/opal/paths.rb +4 -0
  66. data/lib/opal/regexp_anchors.rb +19 -1
  67. data/lib/opal/sprockets.rb +21 -18
  68. data/lib/opal/sprockets/environment.rb +0 -8
  69. data/lib/opal/sprockets/processor.rb +13 -16
  70. data/lib/opal/sprockets/server.rb +6 -12
  71. data/lib/opal/version.rb +1 -1
  72. data/opal.gemspec +1 -0
  73. data/opal/corelib/array.rb +209 -131
  74. data/opal/corelib/basic_object.rb +7 -3
  75. data/opal/corelib/class.rb +11 -17
  76. data/opal/corelib/constants.rb +2 -2
  77. data/opal/corelib/enumerable.rb +178 -355
  78. data/opal/corelib/enumerator.rb +3 -46
  79. data/opal/corelib/error.rb +2 -2
  80. data/opal/corelib/file.rb +13 -1
  81. data/opal/corelib/hash.rb +26 -56
  82. data/opal/corelib/helpers.rb +10 -0
  83. data/opal/corelib/kernel.rb +6 -3
  84. data/opal/corelib/module.rb +62 -31
  85. data/opal/corelib/number.rb +7 -16
  86. data/opal/corelib/proc.rb +24 -9
  87. data/opal/corelib/range.rb +4 -13
  88. data/opal/corelib/runtime.js +515 -378
  89. data/opal/corelib/string.rb +21 -49
  90. data/opal/corelib/struct.rb +50 -35
  91. data/opal/corelib/unsupported.rb +18 -30
  92. data/opal/opal.rb +0 -1
  93. data/opal/opal/mini.rb +1 -0
  94. data/spec/README.md +6 -4
  95. data/spec/filters/bugs/array.rb +0 -42
  96. data/spec/filters/bugs/basicobject.rb +0 -2
  97. data/spec/filters/bugs/bigdecimal.rb +160 -0
  98. data/spec/filters/bugs/class.rb +0 -5
  99. data/spec/filters/bugs/date.rb +1 -48
  100. data/spec/filters/bugs/enumerable.rb +4 -12
  101. data/spec/filters/bugs/enumerator.rb +0 -1
  102. data/spec/filters/bugs/exception.rb +4 -3
  103. data/spec/filters/bugs/float.rb +4 -2
  104. data/spec/filters/bugs/kernel.rb +25 -10
  105. data/spec/filters/bugs/language.rb +119 -68
  106. data/spec/filters/bugs/method.rb +135 -0
  107. data/spec/filters/bugs/module.rb +13 -28
  108. data/spec/filters/bugs/proc.rb +18 -8
  109. data/spec/filters/bugs/range.rb +0 -3
  110. data/spec/filters/bugs/rational.rb +4 -0
  111. data/spec/filters/bugs/regexp.rb +68 -36
  112. data/spec/filters/bugs/string.rb +1 -1
  113. data/spec/filters/bugs/struct.rb +0 -12
  114. data/spec/filters/bugs/time.rb +1 -0
  115. data/spec/filters/bugs/unboundmethod.rb +2 -1
  116. data/spec/filters/unsupported/freeze.rb +3 -1
  117. data/spec/filters/unsupported/language.rb +0 -7
  118. data/spec/filters/unsupported/privacy.rb +7 -6
  119. data/spec/filters/unsupported/string.rb +10 -0
  120. data/spec/filters/unsupported/struct.rb +3 -0
  121. data/spec/filters/unsupported/symbol.rb +9 -0
  122. data/spec/filters/unsupported/taint.rb +0 -3
  123. data/spec/filters/unsupported/thread.rb +1 -0
  124. data/spec/lib/cli_runners/phantomjs_spec.rb +39 -0
  125. data/spec/lib/cli_spec.rb +42 -1
  126. data/spec/lib/compiler/call_spec.rb +700 -0
  127. data/spec/lib/compiler_spec.rb +46 -28
  128. data/spec/lib/config_spec.rb +13 -0
  129. data/spec/lib/parser/call_spec.rb +18 -0
  130. data/spec/lib/parser/def_spec.rb +29 -0
  131. data/spec/lib/parser/iter_spec.rb +15 -15
  132. data/spec/lib/parser/lambda_spec.rb +153 -12
  133. data/spec/lib/parser/string_spec.rb +5 -0
  134. data/spec/lib/parser/undef_spec.rb +1 -1
  135. data/spec/lib/parser/variables_spec.rb +24 -0
  136. data/spec/lib/paths_spec.rb +12 -5
  137. data/spec/lib/spec_helper.rb +5 -0
  138. data/spec/lib/sprockets/processor_spec.rb +6 -5
  139. data/spec/lib/sprockets_spec.rb +8 -0
  140. data/spec/mspec-opal/formatters.rb +188 -0
  141. data/spec/mspec-opal/runner.rb +193 -0
  142. data/spec/opal/core/enumerator/with_index_spec.rb +6 -0
  143. data/spec/opal/core/kernel/define_singleton_method_spec.rb +1 -1
  144. data/spec/opal/core/kernel/instance_variables_spec.rb +14 -0
  145. data/spec/opal/core/kernel/loop_spec.rb +1 -1
  146. data/spec/opal/core/kernel/raise_spec.rb +1 -1
  147. data/spec/opal/core/language/heredoc_spec.rb +42 -0
  148. data/spec/opal/core/language/rescue_spec.rb +18 -0
  149. data/spec/opal/core/language_spec.rb +22 -0
  150. data/spec/opal/core/module/const_defined_spec.rb +1 -2
  151. data/spec/opal/core/module/name_spec.rb +6 -0
  152. data/spec/opal/core/runtime/bridged_classes_spec.rb +14 -2
  153. data/spec/opal/core/runtime/rescue_spec.rb +12 -2
  154. data/spec/opal/core/runtime/super_spec.rb +1 -0
  155. data/spec/opal/core/string_spec.rb +21 -0
  156. data/spec/opal/stdlib/js_spec.rb +1 -1
  157. data/spec/opal/stdlib/native/hash_spec.rb +7 -0
  158. data/spec/opal/stdlib/promise/always_spec.rb +24 -5
  159. data/spec/opal/stdlib/promise/rescue_spec.rb +15 -6
  160. data/spec/opal/stdlib/promise/then_spec.rb +13 -5
  161. data/spec/opal/stdlib/promise/trace_spec.rb +5 -6
  162. data/spec/opal/stdlib/strscan/scan_spec.rb +1 -1
  163. data/spec/ruby_specs +122 -0
  164. data/spec/spec_helper.rb +3 -15
  165. data/stdlib/base64.rb +51 -121
  166. data/stdlib/bigdecimal.rb +231 -0
  167. data/stdlib/bigdecimal/bignumber.js.rb +11 -0
  168. data/stdlib/bigdecimal/kernel.rb +5 -0
  169. data/stdlib/date.rb +252 -10
  170. data/stdlib/native.rb +38 -38
  171. data/stdlib/nodejs/dir.rb +8 -6
  172. data/stdlib/nodejs/file.rb +28 -3
  173. data/stdlib/nodejs/node_modules/.bin/js-yaml +1 -0
  174. data/stdlib/nodejs/node_modules/js-yaml/node_modules/.bin/esparse +1 -0
  175. data/stdlib/nodejs/node_modules/js-yaml/node_modules/.bin/esvalidate +1 -0
  176. data/stdlib/nodejs/require.rb +1 -1
  177. data/stdlib/nodejs/yaml.rb +3 -2
  178. data/stdlib/opal-parser.rb +7 -2
  179. data/stdlib/pathname.rb +23 -1
  180. data/stdlib/phantomjs.rb +10 -0
  181. data/stdlib/promise.rb +38 -23
  182. data/tasks/building.rake +3 -3
  183. data/tasks/testing.rake +27 -14
  184. data/tasks/testing/mspec_special_calls.rb +1 -1
  185. data/tasks/testing/sprockets-phantomjs.js +4 -0
  186. data/test/opal/test_keyword.rb +110 -110
  187. data/test/opal/unsupported_and_bugs.rb +30 -0
  188. data/vendored-minitest/minitest/assertions.rb +1 -1
  189. metadata +65 -15
  190. data/.spectator +0 -2
  191. data/.spectator-mspec +0 -3
  192. data/opal/corelib/array/inheritance.rb +0 -127
  193. data/spec/rubyspecs +0 -139
data/CODE_OF_CONDUCT.md CHANGED
@@ -1,15 +1,13 @@
1
- # Contributor Code of Conduct
2
-
3
- As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
-
5
- We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
-
7
- Examples of unacceptable behavior by participants include the use of overtly sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
-
9
- Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
-
11
- This code of conduct applies both within project spaces and in public spaces where an individual explicitly associates their presence with the project; non-project related material on accounts explicitly marked as personal should not be considered to be so associated.
12
-
13
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
14
-
15
- This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
1
+ This document provides community guidelines for a safe, respectful, productive,
2
+ and collaborative place for any person who is willing to contribute to the Opal
3
+ community. It applies to all “collaborative space”, which is defined as
4
+ community communications channels (such as mailing lists, submitted patches,
5
+ commit comments, etc.).
6
+
7
+ * Participants will be tolerant of opposing views.
8
+ * Participants must ensure that their language and actions are free of personal
9
+ attacks and disparaging personal remarks.
10
+ * When interpreting the words and actions of others, participants should always
11
+ assume good intentions.
12
+ * Behaviour which can be reasonably considered harassment will not be
13
+ tolerated.
data/CONTRIBUTING.md CHANGED
@@ -1,237 +1,47 @@
1
- # Contributing
1
+ # Contributing and Reporting
2
2
 
3
3
  This is the issue tracker for Opal. If you have a more general question about
4
- using opal (or related libraries) then use the
5
- [google group for opal](http://groups.google.com/forum/#!forum/opalrb), or the
6
- [#opal](http://webchat.freenode.net/?channels=opal) irc channel on
7
- FreeNode.
4
+ using Opal (or related libraries) then use the [stackoverflow tag (#opalrb)][SO], the
5
+ [#opal][freenode] irc channel on *FreeNode*, or the *Gitter* chatroom at [opal/opal][gitter]
6
+ (also available as IRC at `irc.gitter.im`).
8
7
 
9
- ## Contributing (TL;DR)
8
+ [SO]: http://stackoverflow.com/questions/ask?tags=opalrb
9
+ [freenode]: http://webchat.freenode.net/?channels=opal
10
+ [gitter]: https://gitter.im/opal/opal
10
11
 
11
- 1. Before opening a new issue, search for previous discussions including closed
12
- ones. Add comments there if a similar issue is found.
12
+ What follows is a quick checklist you can before sending issues or pull-requests, for in-depth instructions on how to hack the internals of Opal and setup the development environment please see [`HACKING.md`][hacking].
13
13
 
14
- 2. Please report the version on which the issue is found.
14
+ [hacking]: https://github.com/opal/opal/blob/master/HACKING.md
15
15
 
16
- 3. Before sending pull requests make sure all tests run and pass (see below).
16
+ ## Submitting a New Issue
17
17
 
18
- 4. Make sure to use a similar coding style to the rest of the code base. In Ruby
19
- and JavaScript code we use 2 spaces (no tabs).
18
+ 1. Before opening a new issue, search for previous discussions including closed
19
+ ones. Add comments there if a similar issue is found.
20
20
 
21
- 5. Make sure to have updated all the relevant documentation, both for API and
22
- the guides.
21
+ 2. Please report the version on which the issue is found (`opal -v`).
23
22
 
24
- If unsure about having satisfied any of the above points ask in the [Gitter channel](https://gitter.im/opal/opal) or just open the issue/pull-request asking for help. There's a good chance someone will help you through the necessary steps.
23
+ 3. The best issues have steps to reproduce the error. Common ways to do that are:
24
+ - A snippet of Ruby code
25
+ - A CLI command with its output, e.g. `opal -ve 'p String != Symbol' # => false'`
25
26
 
26
- **A note on squashing commits in pull-requests:** There's no need to have a single commit in a PR, rather it's better to have focused commits with specific changes and just avoid the sequence of changes and reverts that tell an interesting story but make the use of `git blame` quite problematic. _That said these are quite loose requirements in the spirit of keeping contributing enjoyable 🤓_
27
27
 
28
- ## Quick Start
28
+ ## Submitting a Pull Request
29
29
 
30
- Fork https://github.com/opal/opal, then clone the fork to your machine:
30
+ 1. Before sending pull requests make sure all tests run and pass (see `HACKING.md` in this repo).
31
31
 
32
- ```
33
- $ git clone git://github.com/<Your GitHub Username>/opal.git
34
- ```
32
+ 2. Make sure to use a similar coding style to the rest of the code base. Some examples follow:
33
+ - In Ruby and JavaScript code we use 2 spaces (no tabs)
34
+ - In JavaScript we use `snake_case` for methods and variables
35
35
 
36
- Get dependencies:
36
+ 3. Make sure to have updated all the relevant documentation, both for API (using _yardoc_ syntax) and the Guides
37
37
 
38
- ```
39
- $ bundle install
40
- $ npm install -g jshint
41
- ```
38
+ 4. Add a Changelog entry at the top of `CHANGELOG.md`
42
39
 
43
- RubySpec related repos must be cloned as git submodules:
44
40
 
45
- ```
46
- $ git submodule update --init
47
- ```
41
+ ### A note on commits in PR
48
42
 
49
- Run the test suite:
43
+ You could be asked to squash your commits during a PR review. That doesn't mean there's a preference for a single commit for each PR, rather it's a request to have each commit focused on a specific group changes and avoid the sequence of changes, fixups and reverts that tell an interesting story but in the end make the use of `git blame` quite difficult.
50
44
 
51
- ```
52
- $ bundle exec rake
53
- ```
45
+ __That said, these are quite loose requirements in the spirit of keeping contributing enjoyable 🤓__
54
46
 
55
- You are now ready to make your first contribution to Opal! At a high level, your workflow will be to:
56
47
 
57
- 1. Make changes to Opal source code
58
- 2. Run the test suite to make sure it still passes
59
- 3. Submit a pull request
60
-
61
- ## Down The Rabbit Hole
62
-
63
- Before making changes to Opal source, you need to understand a little about how the test suite works. Every spec that Opal test suite executes is listed in `spec/rubyspecs` file. Each line in that file is a path to either a spec file or a directory full of spec files. If it's a path to a directory, all spec files in that directory will be executed when you run the test suite. Lines starting with a `!` represent files that are excluded (i.e. "execute all files in a given directory, *except* this file"), and lines starting with a `#` are ignored as comments. All paths are relative to the top-level `specs` directory. Let's follow one of these paths - `rubyspec/core/string/sub_spec` - and see where it goes.
64
-
65
- Navigating to `spec/rubyspec/core` directory, you see that it contains multiple sub-directories, usually named after the Ruby class or module. Drilling further down into `spec/rubyspec/core/string` you see all the spec files for the various `String` behaviors under test, usually named by a method name followed by `_spec.rb`. Opening `spec/rubyspec/core/string/sub_spec.rb` you finally see the code that checks the correctness of Opal's implementation of the `String#sub` method's behavior.
66
-
67
- When you execute `$ bundle exec rake`, the code in this file is executed, along with all the other specs in the entire test suite. It's a good idea to run the entire test suite when you feel you reached a certain milestone in the course of making your changes (exactly what that means is up to you), and definitely do `$ bundle exec rake` before commiting your changes to make sure they have not introduced regressions or other unintended side effects.
68
-
69
- But you will want to run tests as often as possible, after every small change, and running the entire test suite will slow you down. You need to be able to execute a single spec that is concerned with the feature you are currently working on. To accomplish this, just add `PATTERN` to your spec invocation command, like this:
70
-
71
- ```
72
- $ bundle exec rake mspec_rubyspec_node PATTERN=spec/rubyspec/core/string/sub_spec.rb
73
- ```
74
-
75
- This will make sure that only `spec/rubyspec/core/string/sub_spec.rb` is run, and no other specs are executed. Globs can be used too:
76
-
77
- ```
78
- $ bundle exec rake mspec_rubyspec_node PATTERN=spec/rubyspec/core/string/*_spec.rb
79
- ```
80
-
81
- Another way to quickly validate ideas and play with your changes is to use `opal-repl`, a tool similar to `irb`. Running `opal-repl` drops you into an interactive environment with your current version of Opal loaded, including any changes you have made.
82
-
83
- ```
84
- $ bundle exec opal-repl
85
- >> 2 + 2
86
- => 4
87
- >>
88
- ```
89
-
90
- When quickly iterating on an idea, even `opal-repl` may feel a bit too heavy, because after making a change in Opal, you must `exit` from `opal-repl` and do `$ bundle exec opal-repl` again to load Opal with your latest changes. In this case, you can run `opal` with the `-e` option, which executes a piece of code you pass to it once, then returns to the shell. This means that in order to run it again after making another adjustment to Opal, all you have to do is hit the up arrow key on your keyboard and press the enter key. This is the fastest way to go from making a change in Opal to seeing its effect.
91
- ```
92
- $ bundle exec opal -e "3.times {puts 'hello'}"
93
- hello
94
- hello
95
- hello
96
- $
97
- ```
98
-
99
- Let's recap what we covered so far. `spec/rubyspecs` is the "master list" of all the specs that get executed when you do `$ bundle exec rake`. You know where to find individual specs, inspect them, and execute them selectively or in bulk. But how do you know which specs to work on? You may be tempted to compare the contents of one of the directories in `spec/rubyspec/core` with the list of paths in `spec/rubyspecs`, add the missing paths to the "master list", run `$ bundle exec rake`, and start fixing the failures by implementing the missing features. However, chances are that as you are reading this, there are plenty of failing tests in the specs that are already listed in `spec/rubyspecs`. How can that be if `$ bundle exec rake` runs green? To understand this, you need to get acquainted with the concept of spec filters.
100
-
101
- There are two types of spec filters in the Opal project: `spec/filters/bugs` and `spec/filters/unsupported`. Both filters have the same effect: any spec failures that are noted inside any of the files inside of these directories are ignored when running the spec suite, i.e. they are not reported as failures. Even though their effect is the same, the purpose of `bugs` and `unsupported` filters is different. As the name suggests, `unsupported` filters list _permanent_ failures, things that other Ruby implementations can do that Opal cannot and will never be able to do (by design and by virtue of being implemented on top of JavaScript running in the browser environment). `bugs` filters, on the other hand, are _temporary_ failures, problems that need to be worked on. Problems that Opal needs your help with. Think of the `bugs` directory and the files contained within it as your "TO DO" list for contributing to Opal.
102
-
103
- Comment out any of the `fail` lines in any of the files in the `spec/filters/bugs` directory, run `$bundle exec rake`, and watch it fail. Make it pass and submit a pull request - that's all there is to it :) Happy hacking!
104
-
105
- ## Benchmarking
106
-
107
- There are two ways to benchmark Opal's performance: one way is to write a program (or a set of programs) that takes sufficently long time to execute, then measure the execution time, and the other is to execute a specific RubySpec example (or a set of examples) multiple times, then measure the execution time. Let's call the former "traditional benchmarking", and the latter "RubySpec benchmarking".
108
-
109
- Regardless of which of the two types of benchmarking above you happen to be doing, the reporting of benchmark results works the same way: `bundle exec rake bench:report`.
110
-
111
- It's important to understand that benchmarking in Opal works on the principle of a single, shared benchmarking workspace, a *bench*, where the results of each benchmark run that you perform get automatically saved. When you do `bundle exec rake bench:report`, you get a combined report of all of the benchmark results that are currently sitting in your workspace. This means you can check out an older commit, run benchmarks, checkout a newer commit, run benchmarks, then run the report to see the results from the two commits side-by-side. After you're done, (or before starting a new benchmarking session), you can do `bundle exec rake bench:clear` to reset your workspace to a clean slate.
112
-
113
- You can get a list of all the available benchmarking commands by running `bundle exec rake -T | grep bench` as shown below.
114
- ```
115
- $ bundle exec rake -T | grep bench
116
-
117
- rake bench:clear # Delete all benchmark results
118
- rake bench:opal # Benchmark Opal
119
- rake bench:report # Combined report of all benchmark results
120
- rake bench:ruby # Benchmark Ruby
121
- ```
122
-
123
- ### Traditional Benchmarking
124
-
125
- At the root of the opal project tree is a folder called `benchmark` that contains a file called `benchmarks`. This file lists all of the benchmarks that will be run if you do `bundle exec bench:opal` without specifying any particular benchmark file(s) as parameters to this rake task. In the example below, I pick which benchmarks to run by passing their file paths as parameters to the rake task.
126
-
127
- Start with a clean slate:
128
- ```
129
- $ bundle exec rake bench:clear
130
-
131
- rm tmp/bench/*
132
- ```
133
-
134
- Run two benchmark programs from the MRI benchmarking suite by passing their file paths as parameters:
135
- (Note: passing params to Rake tasks is tricky - notice there is no space after the comma!)
136
- ```
137
- $ bundle exec rake bench:opal[test/cruby/benchmark/bm_app_answer.rb,test/cruby/benchmark/bm_app_factorial.rb]
138
-
139
- bundle exec opal benchmark/run.rb test/cruby/benchmark/bm_app_answer.rb test/cruby/benchmark/bm_app_factorial.rb | tee tmp/bench/Opal1
140
- test/cruby/benchmark/bm_app_answer.rb 0.7710001468658447
141
- test/cruby/benchmark/bm_app_factorial.rb 0.0820000171661377
142
- ===============================================
143
- Executed 2 benchmarks in 0.8530001640319824 sec
144
- ```
145
-
146
- In this case, I want to see how Opal's results stack up against MRI's results, so I will run the same set of benchmarks for Ruby:
147
- ```
148
- $ bundle exec rake bench:ruby[test/cruby/benchmark/bm_app_answer.rb,test/cruby/benchmark/bm_app_factorial.rb]
149
-
150
- bundle exec ruby benchmark/run.rb test/cruby/benchmark/bm_app_answer.rb test/cruby/benchmark/bm_app_factorial.rb | tee tmp/bench/Ruby1
151
- test/cruby/benchmark/bm_app_answer.rb 0.04913724200014258
152
- test/cruby/benchmark/bm_app_factorial.rb 1.3288652799965348
153
- ===============================================
154
- Executed 2 benchmarks in 1.3780025219966774 sec
155
- ```
156
-
157
- Now I'm ready to see the result of the two runs side-by-side:
158
- ```
159
- $ bundle exec rake bench:report
160
-
161
- Benchmark Opal1 Ruby1
162
- test/cruby/benchmark/bm_app_answer.rb 0.771 0.049
163
- test/cruby/benchmark/bm_app_factorial.rb 0.082 1.329
164
- ```
165
-
166
- If I were to continue running benchmarks, more columns would be added to the report. You can select which columns you want to display (and in what order) by passing their names as params to the rake task like so: `bundle exec rake bench:report[Ruby1,Opal1]`
167
-
168
- ### RubySpec Benchmarking
169
-
170
- This type of benchmarking relies on a feature of MSpec whereby you can ask it to execute every example in a given spec multiple times. Adding `BM=<number of times>` to your regular spec suite invocation command will hook into this MSpec functionality, collect timing information, and dump the results into the benchmarking workspace, making them available for reporting. Below is an example run with a single spec and `BM` set to `100`, meaning each example in the spec would be run 100 times.
171
-
172
- ```
173
- $ bundle exec rake mspec_rubyspec_node PATTERN=spec/rubyspec/core/array/permutation_spec.rb BM=100
174
-
175
- ...
176
-
177
- Benchmark results have been written to tmp/bench/Spec1
178
- To view the results, run bundle exec rake bench:report
179
- ```
180
-
181
- Now let's see the report:
182
- (Spec names can be very long, scroll to the right to see the numbers)
183
- ```
184
- $ bundle exec rake bench:report
185
- Benchmark Spec1
186
- Array#permutation_returns_an_Enumerator_of_all_permutations_when_called_without_a_block_or_arguments 0.117
187
- Array#permutation_returns_an_Enumerator_of_permutations_of_given_length_when_called_with_an_argument_but_no_block 0.064
188
- Array#permutation_yields_all_permutations_to_the_block_then_returns_self_when_called_with_block_but_no_arguments 0.076
189
- Array#permutation_yields_all_permutations_of_given_length_to_the_block_then_returns_self_when_called_with_block_and_argument 0.072
190
- Array#permutation_returns_the_empty_permutation_([[]])_when_the_given_length_is_0 0.029
191
- Array#permutation_returns_the_empty_permutation([])_when_called_on_an_empty_Array 0.029
192
- Array#permutation_returns_no_permutations_when_the_given_length_has_no_permutations 0.029
193
- Array#permutation_handles_duplicate_elements_correctly 0.081
194
- Array#permutation_handles_nested_Arrays_correctly 0.085
195
- Array#permutation_truncates_Float_arguments 0.063
196
- Array#permutation_returns_an_Enumerator_which_works_as_expected_even_when_the_array_was_modified 0.056
197
- Array#permutation_generates_from_a_defensive_copy,_ignoring_mutations 0.038
198
- ```
199
-
200
-
201
- ## Parser
202
-
203
- ## Enabling debug output from Racc
204
-
205
- To enable debug output in the Racc grammar set the `RACC_DEBUG` env var and recompile the grammar. While the env variable is set the parser will output the debug info for the parsing process.
206
-
207
- ```bash
208
- $ export RACC_DEBUG=true
209
- $ bundle exec rake racc # recompile the grammar
210
- $ bundle exec bin/opal --sexp -e "42" # ask Opal for the SExp of some code
211
-
212
- read :tINTEGER(tINTEGER) [42, [1, 0]]
213
-
214
- shift tINTEGER
215
- [ (tINTEGER [42, [1, 0]]) ]
216
-
217
- …cut…
218
-
219
- shift $end
220
- [ (program (:int, 42)) ($end [false, [1, 2]]) ($end [false, [1, 2]]) ]
221
-
222
- goto 403
223
- [ 0 1 97 403 ]
224
-
225
- accept
226
-
227
- (:int, 42)
228
- ```
229
-
230
- When done unset the env variable and recompile the grammar.
231
-
232
- ```bash
233
- $ unset RACC_DEBUG
234
- $ bundle exec rake racc # recompile the grammar
235
- $ bundle exec bin/opal --sexp -e "42" # ask Opal for the SExp of some code
236
- (:int, 42)
237
- ```
data/Gemfile CHANGED
@@ -1,27 +1,35 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
+ tilt_version = ENV['TILT_VERSION']
5
+ rack_version = ENV['RACK_VERSION']
6
+
4
7
  # Stick with older racc until
5
8
  # https://github.com/tenderlove/racc/issues/22
6
9
  # is solved.
7
- gem 'racc', '< 1.4.10' if RUBY_ENGINE == 'jruby'
8
- gem 'json', '< 1.8.1' if RUBY_VERSION.to_f == 2.1 and RUBY_ENGINE == 'ruby'
9
- gem 'rubysl', :platform => :rbx
10
- gem 'thin', platform: :mri
10
+ gem 'racc', '< 1.4.10', platform: :jruby
11
+ gem 'json', '< 1.8.1', platform: :ruby if RUBY_VERSION.to_f == 2.1
12
+ gem 'rubysl', platform: :rbx
11
13
 
12
- group :repl do
13
- gem 'therubyracer', :platform => :mri, :require => 'v8'
14
- gem 'therubyrhino', :platform => :jruby
15
- end
14
+ # thin requires rack < 2
15
+ gem 'thin', platform: :mri if !rack_version || (rack_version < '2')
16
16
 
17
- tilt_version = ENV['TILT_VERSION']
17
+ gem 'rack', rack_version if rack_version
18
18
  gem 'tilt', tilt_version if tilt_version
19
19
 
20
+ group :repl do
21
+ gem 'therubyracer', platform: :mri, require: 'v8'
22
+ gem 'therubyrhino', platform: :jruby
23
+ end
24
+
20
25
  unless ENV['CI']
21
26
  gem 'rb-fsevent'
22
27
  gem 'guard', require: false
23
- gem 'terminal-notifier-guard'
28
+
29
+ if RUBY_PLATFORM =~ /darwin/
30
+ gem 'terminal-notifier-guard'
31
+ gem 'terminal-notifier'
32
+ end
24
33
  end
25
34
 
26
- gem 'mspec', github: 'ruby/mspec', ref: '7cb4c8310677e41e48a78bc0b0029faf7a74c3c8'
27
- gem 'listen', '< 3.1'
35
+ gem 'mspec', path: 'spec/mspec'
data/Guardfile CHANGED
@@ -51,10 +51,10 @@ class ::Guard::Opal < Plugin
51
51
  case path
52
52
  when %r{grammar\.y$} then system 'rake racc'
53
53
  when %r{^spec/lib} then rspec path
54
- when %r{^spec/rubyspec} then mspec path
54
+ when %r{^spec/ruby} then mspec path
55
55
  when %r{^opal/corelib}
56
56
  name = File.basename(path, '.rb')
57
- mspec "spec/rubyspec/core/#{name}/**/*_spec.rb"
57
+ mspec "spec/ruby/core/#{name}/**/*_spec.rb"
58
58
  when %r{^lib/opal/(.*)\.rb$}
59
59
  name = $1
60
60
  specs = Dir["spec/lib/#{name}_spec.rb"]
data/HACKING.md ADDED
@@ -0,0 +1,230 @@
1
+ # Hacking
2
+
3
+ ## Quick Start
4
+
5
+ [Fork opal/opal on GitHub](https://github.com/opal/opal/fork), then clone the fork to your machine:
6
+
7
+ ```
8
+ $ git clone git://github.com/<YOUR-GITHUB-USERNAME>/opal.git
9
+ ```
10
+
11
+ The Ruby Spec Suite related repos must be cloned as git submodules:
12
+
13
+ ```
14
+ $ git submodule update --init
15
+ ```
16
+
17
+ Get dependencies:
18
+
19
+ ```
20
+ $ bundle install
21
+ $ npm install -g jshint
22
+ ```
23
+
24
+ Run the test suite:
25
+
26
+ ```
27
+ $ bundle exec rake
28
+ ```
29
+
30
+ You are now ready to make your first contribution to Opal! At a high level, your workflow will be to:
31
+
32
+ 1. Make changes to Opal source code
33
+ 2. Run the test suite to make sure it still passes
34
+ 3. Submit a pull request
35
+
36
+
37
+ ## Down The Rabbit Hole
38
+
39
+ Before making changes to Opal source, you need to understand a little about how the test suite works. Every spec that Opal test suite executes is listed in `spec/ruby_specs` file. Each line in that file is a path to either a spec file or a directory full of spec files. If it's a path to a directory, all spec files in that directory will be executed when you run the test suite. Lines starting with a `!` represent files that are excluded (i.e. "execute all files in a given directory, *except* this file"), and lines starting with a `#` are ignored as comments. All paths are relative to the top-level `specs` directory. Let's follow one of these paths - `ruby/core/string/sub_spec` - and see where it goes.
40
+
41
+ Navigating to `spec/ruby/core` directory, you see that it contains multiple sub-directories, usually named after the Ruby class or module. Drilling further down into `spec/ruby/core/string` you see all the spec files for the various `String` behaviors under test, usually named by a method name followed by `_spec.rb`. Opening `spec/ruby/core/string/sub_spec.rb` you finally see the code that checks the correctness of Opal's implementation of the `String#sub` method's behavior.
42
+
43
+ When you execute `$ bundle exec rake`, the code in this file is executed, along with all the other specs in the entire test suite. It's a good idea to run the entire test suite when you feel you reached a certain milestone in the course of making your changes (exactly what that means is up to you), and definitely do `$ bundle exec rake` before commiting your changes to make sure they have not introduced regressions or other unintended side effects.
44
+
45
+ But you will want to run tests as often as possible, after every small change, and running the entire test suite will slow you down. You need to be able to execute a single spec that is concerned with the feature you are currently working on. To accomplish this, just add `PATTERN` to your spec invocation command, like this:
46
+
47
+ ```
48
+ $ bundle exec rake mspec_ruby_nodejs PATTERN=spec/ruby/core/string/sub_spec.rb
49
+ ```
50
+
51
+ This will make sure that only `spec/ruby/core/string/sub_spec.rb` is run, and no other specs are executed. Globs can be used too:
52
+
53
+ ```
54
+ $ bundle exec rake mspec_ruby_nodejs PATTERN=spec/ruby/core/string/*_spec.rb
55
+ ```
56
+
57
+ Another way to quickly validate ideas and play with your changes is to use `opal-repl`, a tool similar to `irb`. Running `opal-repl` drops you into an interactive environment with your current version of Opal loaded, including any changes you have made.
58
+
59
+ ```
60
+ $ bundle exec opal-repl
61
+ >> 2 + 2
62
+ => 4
63
+ >>
64
+ ```
65
+
66
+ When quickly iterating on an idea, even `opal-repl` may feel a bit too heavy, because after making a change in Opal, you must `exit` from `opal-repl` and do `$ bundle exec opal-repl` again to load Opal with your latest changes. In this case, you can run `opal` with the `-e` option, which executes a piece of code you pass to it once, then returns to the shell. This means that in order to run it again after making another adjustment to Opal, all you have to do is hit the up arrow key on your keyboard and press the enter key. This is the fastest way to go from making a change in Opal to seeing its effect.
67
+
68
+ ```
69
+ $ bundle exec opal -e "3.times {puts 'hello'}"
70
+ hello
71
+ hello
72
+ hello
73
+ $
74
+ ```
75
+
76
+ Let's recap what we covered so far. `spec/ruby_specs` is the "master list" of all the specs that get executed when you do `$ bundle exec rake`. You know where to find individual specs, inspect them, and execute them selectively or in bulk. But how do you know which specs to work on? You may be tempted to compare the contents of one of the directories in `spec/ruby/core` with the list of paths in `spec/ruby_specs`, add the missing paths to the "master list", run `$ bundle exec rake`, and start fixing the failures by implementing the missing features. However, chances are that as you are reading this, there are plenty of failing tests in the specs that are already listed in `spec/ruby_specs`. How can that be if `$ bundle exec rake` runs green? To understand this, you need to get acquainted with the concept of spec filters.
77
+
78
+ There are two types of spec filters in the Opal project: `spec/filters/bugs` and `spec/filters/unsupported`. Both filters have the same effect: any spec failures that are noted inside any of the files inside of these directories are ignored when running the spec suite, i.e. they are not reported as failures. Even though their effect is the same, the purpose of `bugs` and `unsupported` filters is different. As the name suggests, `unsupported` filters list _permanent_ failures, things that other Ruby implementations can do that Opal cannot and will never be able to do (by design and by virtue of being implemented on top of JavaScript running in the browser environment). `bugs` filters, on the other hand, are _temporary_ failures, problems that need to be worked on. Problems that Opal needs your help with. Think of the `bugs` directory and the files contained within it as your "TO DO" list for contributing to Opal.
79
+
80
+ Comment out any of the `fail` lines in any of the files in the `spec/filters/bugs` directory, run `$bundle exec rake`, and watch it fail. Make it pass and submit a pull request - that's all there is to it :) Happy hacking!
81
+
82
+ Core classes use each other and your changes may fix other bugs in `spec/filters/bugs`. If you think it's possible, run an inverted test suite by providing environment variable `INVERT_RUNNING_MODE=true`:
83
+
84
+ ```
85
+ $ env INVERT_RUNNING_MODE=true RUBYSPECS=true PATTERN=spec/ruby/core/string/*_spec.rb rake mspec_ruby_nodejs
86
+ ```
87
+
88
+ This command will execute tests marked as "bugs" from every file in the `spec/ruby/core/string` directory. After running it you will get a list of specs that in fact are passing. Feel free to remove them from `spec/filters/bugs`.
89
+
90
+ Note: Opal has some bugs that may cause a shared state between tests. Sometimes green specs are green only in the inverted test suite, so after removing them from `/bugs`, run a regular test suite one more time to verify that everything is fine.
91
+
92
+ ## Benchmarking
93
+
94
+ There are two ways to benchmark Opal's performance: one way is to write a program (or a set of programs) that takes sufficently long time to execute, then measure the execution time, and the other is to execute a specific Ruby Spec Suite example (or a set of examples) multiple times, then measure the execution time. Let's call the former "Traditional Benchmarking", and the latter "The Ruby Spec Suite Benchmarking".
95
+
96
+ Regardless of which of the two types of benchmarking above you happen to be doing, the reporting of benchmark results works the same way: `bundle exec rake bench:report`.
97
+
98
+ It's important to understand that benchmarking in Opal works on the principle of a single, shared benchmarking workspace, a *bench*, where the results of each benchmark run that you perform get automatically saved. When you do `bundle exec rake bench:report`, you get a combined report of all of the benchmark results that are currently sitting in your workspace. This means you can check out an older commit, run benchmarks, checkout a newer commit, run benchmarks, then run the report to see the results from the two commits side-by-side. After you're done, (or before starting a new benchmarking session), you can do `bundle exec rake bench:clear` to reset your workspace to a clean slate.
99
+
100
+ You can get a list of all the available benchmarking commands by running `bundle exec rake -T | grep bench` as shown below.
101
+
102
+ ```
103
+ $ bundle exec rake -T | grep bench
104
+
105
+ rake bench:clear # Delete all benchmark results
106
+ rake bench:opal # Benchmark Opal
107
+ rake bench:report # Combined report of all benchmark results
108
+ rake bench:ruby # Benchmark Ruby
109
+ ```
110
+
111
+ ### Traditional Benchmarking
112
+
113
+ At the root of the opal project tree is a folder called `benchmark` that contains a file called `benchmarks`. This file lists all of the benchmarks that will be run if you do `bundle exec bench:opal` without specifying any particular benchmark file(s) as parameters to this rake task. In the example below, I pick which benchmarks to run by passing their file paths as parameters to the rake task.
114
+
115
+ Start with a clean slate:
116
+
117
+ ```
118
+ $ bundle exec rake bench:clear
119
+
120
+ rm tmp/bench/*
121
+ ```
122
+
123
+ Run two benchmark programs from the MRI benchmarking suite by passing their file paths as parameters:
124
+ (Note: passing params to Rake tasks is tricky - notice there is no space after the comma!)
125
+
126
+ ```
127
+ $ bundle exec rake bench:opal[test/cruby/benchmark/bm_app_answer.rb,test/cruby/benchmark/bm_app_factorial.rb]
128
+
129
+ bundle exec opal benchmark/run.rb test/cruby/benchmark/bm_app_answer.rb test/cruby/benchmark/bm_app_factorial.rb | tee tmp/bench/Opal1
130
+ test/cruby/benchmark/bm_app_answer.rb 0.7710001468658447
131
+ test/cruby/benchmark/bm_app_factorial.rb 0.0820000171661377
132
+ ===============================================
133
+ Executed 2 benchmarks in 0.8530001640319824 sec
134
+ ```
135
+
136
+ In this case, I want to see how Opal's results stack up against MRI's results, so I will run the same set of benchmarks for Ruby:
137
+
138
+ ```
139
+ $ bundle exec rake bench:ruby[test/cruby/benchmark/bm_app_answer.rb,test/cruby/benchmark/bm_app_factorial.rb]
140
+
141
+ bundle exec ruby benchmark/run.rb test/cruby/benchmark/bm_app_answer.rb test/cruby/benchmark/bm_app_factorial.rb | tee tmp/bench/Ruby1
142
+ test/cruby/benchmark/bm_app_answer.rb 0.04913724200014258
143
+ test/cruby/benchmark/bm_app_factorial.rb 1.3288652799965348
144
+ ===============================================
145
+ Executed 2 benchmarks in 1.3780025219966774 sec
146
+ ```
147
+
148
+ Now I'm ready to see the result of the two runs side-by-side:
149
+
150
+ ```
151
+ $ bundle exec rake bench:report
152
+
153
+ Benchmark Opal1 Ruby1
154
+ test/cruby/benchmark/bm_app_answer.rb 0.771 0.049
155
+ test/cruby/benchmark/bm_app_factorial.rb 0.082 1.329
156
+ ```
157
+
158
+ If I were to continue running benchmarks, more columns would be added to the report. You can select which columns you want to display (and in what order) by passing their names as params to the rake task like so: `bundle exec rake bench:report[Ruby1,Opal1]`
159
+
160
+ ### The Ruby Spec Suite Benchmarking
161
+
162
+ This type of benchmarking relies on a feature of MSpec whereby you can ask it to execute every example in a given spec multiple times. Adding `BM=<number of times>` to your regular spec suite invocation command will hook into this MSpec functionality, collect timing information, and dump the results into the benchmarking workspace, making them available for reporting. Below is an example run with a single spec and `BM` set to `100`, meaning each example in the spec would be run 100 times.
163
+
164
+ ```
165
+ $ bundle exec rake mspec_ruby_nodejs PATTERN=spec/ruby/core/array/permutation_spec.rb BM=100
166
+
167
+ ...
168
+
169
+ Benchmark results have been written to tmp/bench/Spec1
170
+ To view the results, run bundle exec rake bench:report
171
+ ```
172
+
173
+ Now let's see the report:
174
+ (Spec names can be very long, scroll to the right to see the numbers)
175
+
176
+ ```
177
+ $ bundle exec rake bench:report
178
+ Benchmark Spec1
179
+ Array#permutation_returns_an_Enumerator_of_all_permutations_when_called_without_a_block_or_arguments 0.117
180
+ Array#permutation_returns_an_Enumerator_of_permutations_of_given_length_when_called_with_an_argument_but_no_block 0.064
181
+ Array#permutation_yields_all_permutations_to_the_block_then_returns_self_when_called_with_block_but_no_arguments 0.076
182
+ Array#permutation_yields_all_permutations_of_given_length_to_the_block_then_returns_self_when_called_with_block_and_argument 0.072
183
+ Array#permutation_returns_the_empty_permutation_([[]])_when_the_given_length_is_0 0.029
184
+ Array#permutation_returns_the_empty_permutation([])_when_called_on_an_empty_Array 0.029
185
+ Array#permutation_returns_no_permutations_when_the_given_length_has_no_permutations 0.029
186
+ Array#permutation_handles_duplicate_elements_correctly 0.081
187
+ Array#permutation_handles_nested_Arrays_correctly 0.085
188
+ Array#permutation_truncates_Float_arguments 0.063
189
+ Array#permutation_returns_an_Enumerator_which_works_as_expected_even_when_the_array_was_modified 0.056
190
+ Array#permutation_generates_from_a_defensive_copy,_ignoring_mutations 0.038
191
+ ```
192
+
193
+
194
+ ## Parser
195
+
196
+ ## Enabling debug output from Racc
197
+
198
+ To enable debug output in the Racc grammar set the `RACC_DEBUG` env var and recompile the grammar. While the env variable is set the parser will output the debug info for the parsing process.
199
+
200
+ ```bash
201
+ $ export RACC_DEBUG=true
202
+ $ bundle exec rake racc # recompile the grammar
203
+ $ bundle exec bin/opal --sexp -e "42" # ask Opal for the SExp of some code
204
+
205
+ read :tINTEGER(tINTEGER) [42, [1, 0]]
206
+
207
+ shift tINTEGER
208
+ [ (tINTEGER [42, [1, 0]]) ]
209
+
210
+ …cut…
211
+
212
+ shift $end
213
+ [ (program (:int, 42)) ($end [false, [1, 2]]) ($end [false, [1, 2]]) ]
214
+
215
+ goto 403
216
+ [ 0 1 97 403 ]
217
+
218
+ accept
219
+
220
+ (:int, 42)
221
+ ```
222
+
223
+ When done unset the env variable and recompile the grammar.
224
+
225
+ ```bash
226
+ $ unset RACC_DEBUG
227
+ $ bundle exec rake racc # recompile the grammar
228
+ $ bundle exec bin/opal --sexp -e "42" # ask Opal for the SExp of some code
229
+ (:int, 42)
230
+ ```