opal 1.2.0 → 1.3.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.await.js +6 -0
  3. data/.eslintrc.js +34 -0
  4. data/.github/workflows/build.yml +8 -0
  5. data/.rubocop.yml +9 -0
  6. data/CHANGELOG.md +4 -0
  7. data/README.md +1 -1
  8. data/Rakefile +1 -0
  9. data/UNRELEASED.md +64 -38
  10. data/docs/async.md +109 -0
  11. data/docs/roda-sprockets.md +0 -2
  12. data/exe/opal +2 -0
  13. data/exe/opal-repl +2 -2
  14. data/lib/opal/builder.rb +5 -1
  15. data/lib/opal/builder_processors.rb +7 -2
  16. data/lib/opal/cache/file_cache.rb +119 -0
  17. data/lib/opal/cache.rb +71 -0
  18. data/lib/opal/cli.rb +35 -1
  19. data/lib/opal/cli_options.rb +21 -0
  20. data/lib/opal/cli_runners/chrome.rb +21 -14
  21. data/lib/opal/cli_runners/chrome_cdp_interface.js +30285 -0
  22. data/lib/opal/cli_runners/{chrome.js → chrome_cdp_interface.rb} +27 -6
  23. data/lib/opal/cli_runners/compiler.rb +2 -1
  24. data/lib/opal/cli_runners/gjs.rb +27 -0
  25. data/lib/opal/cli_runners/mini_racer.rb +36 -0
  26. data/lib/opal/cli_runners/source-map-support-browser.js +276 -91
  27. data/lib/opal/cli_runners/source-map-support-node.js +276 -91
  28. data/lib/opal/cli_runners/source-map-support.js +60 -18
  29. data/lib/opal/cli_runners.rb +2 -0
  30. data/lib/opal/compiler.rb +99 -10
  31. data/lib/opal/fragment.rb +77 -14
  32. data/lib/opal/nodes/args/extract_kwrestarg.rb +6 -4
  33. data/lib/opal/nodes/args/extract_restarg.rb +10 -12
  34. data/lib/opal/nodes/args.rb +28 -0
  35. data/lib/opal/nodes/base.rb +29 -5
  36. data/lib/opal/nodes/call.rb +123 -2
  37. data/lib/opal/nodes/case.rb +7 -1
  38. data/lib/opal/nodes/class.rb +12 -2
  39. data/lib/opal/nodes/def.rb +3 -23
  40. data/lib/opal/nodes/definitions.rb +21 -4
  41. data/lib/opal/nodes/helpers.rb +2 -2
  42. data/lib/opal/nodes/if.rb +39 -9
  43. data/lib/opal/nodes/iter.rb +15 -3
  44. data/lib/opal/nodes/lambda.rb +3 -1
  45. data/lib/opal/nodes/literal.rb +13 -7
  46. data/lib/opal/nodes/logic.rb +2 -2
  47. data/lib/opal/nodes/module.rb +12 -2
  48. data/lib/opal/nodes/rescue.rb +59 -34
  49. data/lib/opal/nodes/scope.rb +88 -6
  50. data/lib/opal/nodes/super.rb +52 -25
  51. data/lib/opal/nodes/top.rb +13 -7
  52. data/lib/opal/nodes/while.rb +7 -1
  53. data/lib/opal/parser/patch.rb +2 -1
  54. data/lib/opal/repl.rb +137 -49
  55. data/lib/opal/rewriters/binary_operator_assignment.rb +10 -10
  56. data/lib/opal/rewriters/block_to_iter.rb +3 -3
  57. data/lib/opal/rewriters/for_rewriter.rb +7 -7
  58. data/lib/opal/rewriters/js_reserved_words.rb +5 -3
  59. data/lib/opal/source_map/file.rb +7 -4
  60. data/lib/opal/source_map/map.rb +17 -3
  61. data/lib/opal/version.rb +1 -1
  62. data/opal/corelib/array.rb +2 -2
  63. data/opal/corelib/binding.rb +46 -0
  64. data/opal/corelib/boolean.rb +54 -4
  65. data/opal/corelib/class.rb +2 -0
  66. data/opal/corelib/constants.rb +2 -2
  67. data/opal/corelib/error.rb +98 -12
  68. data/opal/corelib/io.rb +250 -38
  69. data/opal/corelib/kernel/format.rb +5 -2
  70. data/opal/corelib/kernel.rb +44 -23
  71. data/opal/corelib/main.rb +5 -0
  72. data/opal/corelib/method.rb +1 -0
  73. data/opal/corelib/module.rb +28 -0
  74. data/opal/corelib/number.rb +12 -1
  75. data/opal/corelib/random/seedrandom.js.rb +2 -2
  76. data/opal/corelib/regexp.rb +47 -3
  77. data/opal/corelib/runtime.js +152 -12
  78. data/opal/corelib/string/encoding.rb +17 -17
  79. data/opal/corelib/string.rb +2 -0
  80. data/opal/corelib/struct.rb +10 -3
  81. data/opal/corelib/trace_point.rb +57 -0
  82. data/opal/opal/full.rb +2 -0
  83. data/package.json +3 -2
  84. data/spec/filters/bugs/array.rb +0 -1
  85. data/spec/filters/bugs/basicobject.rb +0 -1
  86. data/spec/filters/bugs/binding.rb +27 -0
  87. data/spec/filters/bugs/enumerator.rb +132 -0
  88. data/spec/filters/bugs/exception.rb +70 -93
  89. data/spec/filters/bugs/float.rb +0 -1
  90. data/spec/filters/bugs/kernel.rb +3 -9
  91. data/spec/filters/bugs/language.rb +15 -58
  92. data/spec/filters/bugs/main.rb +16 -0
  93. data/spec/filters/bugs/matrix.rb +39 -0
  94. data/spec/filters/bugs/method.rb +0 -2
  95. data/spec/filters/bugs/module.rb +36 -79
  96. data/spec/filters/bugs/proc.rb +0 -1
  97. data/spec/filters/bugs/regexp.rb +0 -16
  98. data/spec/filters/bugs/trace_point.rb +12 -0
  99. data/spec/filters/bugs/warnings.rb +0 -4
  100. data/spec/filters/unsupported/freeze.rb +2 -0
  101. data/spec/filters/unsupported/privacy.rb +4 -0
  102. data/spec/lib/compiler_spec.rb +7 -1
  103. data/spec/lib/repl_spec.rb +4 -2
  104. data/spec/lib/source_map/file_spec.rb +1 -1
  105. data/spec/mspec-opal/formatters.rb +18 -4
  106. data/spec/mspec-opal/runner.rb +2 -2
  107. data/spec/opal/core/boolean_spec.rb +44 -0
  108. data/spec/opal/core/hash_spec.rb +8 -0
  109. data/spec/opal/core/number/to_s_spec.rb +11 -0
  110. data/spec/opal/stdlib/json/ext_spec.rb +3 -3
  111. data/spec/opal/stdlib/logger/logger_spec.rb +10 -1
  112. data/spec/ruby_specs +18 -0
  113. data/stdlib/await.rb +83 -0
  114. data/stdlib/base64.rb +4 -4
  115. data/stdlib/bigdecimal/bignumber.js.rb +4 -2
  116. data/stdlib/bigdecimal.rb +1 -0
  117. data/stdlib/gjs/io.rb +33 -0
  118. data/stdlib/gjs/kernel.rb +5 -0
  119. data/stdlib/gjs.rb +2 -0
  120. data/stdlib/js.rb +4 -0
  121. data/stdlib/json.rb +3 -3
  122. data/stdlib/logger.rb +1 -1
  123. data/stdlib/nashorn/file.rb +2 -0
  124. data/stdlib/nodejs/env.rb +7 -0
  125. data/stdlib/nodejs/file.rb +6 -41
  126. data/stdlib/nodejs/io.rb +21 -5
  127. data/stdlib/nodejs/js-yaml-3-6-1.js +2 -2
  128. data/stdlib/opal/miniracer.rb +6 -0
  129. data/stdlib/opal/platform.rb +4 -0
  130. data/stdlib/opal/repl_js.rb +5 -0
  131. data/stdlib/opal/replutils.rb +271 -0
  132. data/stdlib/opal-parser.rb +24 -11
  133. data/stdlib/opal-platform.rb +8 -0
  134. data/stdlib/promise/v2.rb +16 -4
  135. data/stdlib/promise.rb +14 -0
  136. data/stdlib/stringio.rb +13 -110
  137. data/stdlib/thread.rb +29 -0
  138. data/tasks/building.rake +10 -4
  139. data/tasks/linting-parse-eslint-results.js +39 -0
  140. data/tasks/linting.rake +38 -28
  141. data/tasks/performance/asciidoctor_test.rb.erb +6 -0
  142. data/tasks/performance/optimization_status.rb +77 -0
  143. data/tasks/performance.rake +149 -0
  144. data/tasks/testing.rake +9 -1
  145. data/test/nodejs/test_await.rb +169 -0
  146. data/test/opal/promisev2/test_error.rb +9 -3
  147. data/test/opal/unsupported_and_bugs.rb +5 -0
  148. data/vendored-minitest/minitest/benchmark.rb +9 -7
  149. data/vendored-minitest/minitest/test.rb +14 -12
  150. data/vendored-minitest/minitest.rb +19 -16
  151. data/yarn.lock +686 -117
  152. metadata +60 -23
  153. data/.jshintrc +0 -41
  154. data/spec/filters/unsupported/refinements.rb +0 -8
  155. data/vendored-minitest/minitest/hell.rb +0 -11
  156. data/vendored-minitest/minitest/parallel.rb +0 -65
  157. data/vendored-minitest/minitest/pride.rb +0 -4
  158. data/vendored-minitest/minitest/pride_plugin.rb +0 -142
  159. data/vendored-minitest/minitest/unit.rb +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f13610ebfa130a3b3379a2d23f3fc077de87e4fd69c33248a87bdf23e144686a
4
- data.tar.gz: 275fa37ed3f201f2c716bcf8f05cc5fcd5ff42dec5aaae1b4753c126a629d5c1
3
+ metadata.gz: cf895e8ef63c69f90e6e099aa2f4c671649411893b980bdb03b55268da2e0a34
4
+ data.tar.gz: 5fc847637cea4a857bcc78ad1a491d5c898fdc051d779644f39e282e6c2aba66
5
5
  SHA512:
6
- metadata.gz: b5a9d1f1494542fd5d1669bb16548b5a335777398a3d84945dbabb8477cedd79f1fd1fc251f663e9f22a518a9a7c3d361f83fb14c860bc3cb36b79c7cd6c40b8
7
- data.tar.gz: 90f237673bc19937ee643fbe5562cd348476f975a90348c95138785457a6da342b4df98a48d14b223a938e9c91ac603fde0d2c5cbe18982ec1bcc3c1e6d4c375
6
+ metadata.gz: 0eb66e6cb09d52075f7afd1ab8d10b67fdab302a503269f95b152440991b8d1f9bc3fc759e0265503350e6c4cd3c1453afca9d7d971bbdae46a367f15f5f8230
7
+ data.tar.gz: e0ce987c98345a13101dc3d9311ce1bb41435777edf41ab14aac96cf5821c1e6076236de25216c443fa806567af6f1a32de2f8d7d9e1e67e4ba165547c938e95
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ "extends": "./.eslintrc.js",
3
+ "parserOptions": {
4
+ "ecmaVersion": 8
5
+ },
6
+ };
data/.eslintrc.js ADDED
@@ -0,0 +1,34 @@
1
+ module.exports = {
2
+ "env": {
3
+ "browser": true,
4
+ "node": true
5
+ },
6
+ "extends": "eslint:recommended",
7
+ "parserOptions": {
8
+ "ecmaVersion": 3
9
+ },
10
+ "rules": {
11
+ "no-unused-vars": ["error", {
12
+ "varsIgnorePattern": "(\$(\$|\$\$|yield|post_args|[a-z])|self)",
13
+ "argsIgnorePattern": "(\$(\$|\$\$|yield|post_args|[a-z])|self)",
14
+ }],
15
+ "no-extra-semi": "off",
16
+ "no-empty": "off",
17
+ "no-unreachable": "off",
18
+ "no-cond-assign": "off",
19
+ "no-prototype-builtins": "off",
20
+ "no-constant-condition": ["error", { "checkLoops": false }],
21
+ "no-useless-escape": "off",
22
+ "no-fallthrough": ["error", { "commentPattern": "raise|no-break" }],
23
+ "no-regex-spaces": "off",
24
+ "no-control-regex": "off",
25
+ },
26
+ "globals": {
27
+ "Opal": "readonly",
28
+ "DataView": "readonly",
29
+ "ArrayBuffer": "readonly",
30
+ "globalThis": "readonly",
31
+ "Uint8Array": "readonly",
32
+ "Promise": "readonly",
33
+ }
34
+ };
@@ -49,6 +49,11 @@ jobs:
49
49
  ruby: 3.0
50
50
  - name: timezone
51
51
  ruby: 3.0
52
+ - name: performance
53
+ ruby: 3.0
54
+ permissive: true
55
+ fetchdepth: '0'
56
+ command: bin/rake performance:compare
52
57
 
53
58
  # Currently failing:
54
59
  # - ruby: truffleruby
@@ -56,8 +61,11 @@ jobs:
56
61
  # - ruby: ruby-head
57
62
 
58
63
  runs-on: ${{ matrix.combo.os || 'ubuntu-latest' }}
64
+ continue-on-error: ${{ matrix.combo.permissive || false }}
59
65
  steps:
60
66
  - uses: actions/checkout@v2
67
+ with:
68
+ fetch-depth: ${{ fromJSON(matrix.combo.fetchdepth || '1') }}
61
69
  - uses: ruby/setup-ruby@v1
62
70
  with:
63
71
  ruby-version: ${{ matrix.combo.ruby }}
data/.rubocop.yml CHANGED
@@ -71,6 +71,7 @@ Layout/CommentIndentation:
71
71
  Exclude:
72
72
  - 'lib/opal/rewriters/binary_operator_assignment.rb'
73
73
  - 'lib/opal/rewriters/logical_operator_assignment.rb'
74
+ - 'lib/opal/source_map/file.rb'
74
75
 
75
76
  # We need to support older rubies
76
77
  Layout/IndentHeredoc:
@@ -92,6 +93,7 @@ Style/GlobalVars:
92
93
  - 'stdlib/nodejs/irb.rb'
93
94
  - 'stdlib/console.rb'
94
95
  - 'stdlib/native.rb'
96
+ - 'stdlib/await.rb'
95
97
 
96
98
  Layout/ExtraSpacing:
97
99
  Exclude:
@@ -124,6 +126,11 @@ Lint/LiteralAsCondition:
124
126
  - 'opal/**/*.rb'
125
127
  - 'stdlib/**/*.rb'
126
128
 
129
+ Lint/Loop:
130
+ Exclude:
131
+ # This is for optimization purposes mostly
132
+ - 'opal/corelib/io.rb'
133
+
127
134
  # Allow the use of if/unless inside blocks
128
135
  Style/Next:
129
136
  Enabled: false
@@ -134,6 +141,7 @@ Lint/RescueException:
134
141
  - 'opal/corelib/enumerator.rb'
135
142
  # Promises must care about all exceptions
136
143
  - 'stdlib/promise.rb'
144
+ - 'opal/corelib/binding.rb'
137
145
 
138
146
  Lint/StringConversionInInterpolation:
139
147
  Exclude:
@@ -412,6 +420,7 @@ Style/ConditionalAssignment:
412
420
  Naming/MemoizedInstanceVariableName:
413
421
  Exclude:
414
422
  - lib/opal/parser/patch.rb # it's a monkey-patch on the parser gem
423
+ - lib/opal/nodes/rescue.rb # we know what we are doing here and no, it's not memoization
415
424
 
416
425
  Style/AccessModifierDeclarations:
417
426
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -30,6 +30,9 @@ Changes are grouped as follows:
30
30
  - Support for `"\x80"` syntax in String literals ([#2235](https://github.com/opal/opal/pull/2235))
31
31
  - Added `String#+@`, `String#-@` ([#2235](https://github.com/opal/opal/pull/2235))
32
32
  - Support for `begin <CODE> end while <CONDITION>` ([#2255](https://github.com/opal/opal/pull/2255))
33
+ - Added Hash#except and `Hash#except!` ([#2243](https://github.com/opal/opal/pull/2243))
34
+ - Parser 3.0: Implement pattern matching (as part of this `{Array,Hash,Struct}#{deconstruct,deconstruct_keys} methods were added)` ([#2243](https://github.com/opal/opal/pull/2243))
35
+ - [experimental] Reimplement Promise to make it bridged with JS native Promise, this new implementation can be used by requiring `promise/v2` ([#2220](https://github.com/opal/opal/pull/2220))
33
36
 
34
37
  ### Fixed
35
38
 
@@ -48,6 +51,7 @@ Changes are grouped as follows:
48
51
  - Rework class variables to support inheritance correctly ([#2251](https://github.com/opal/opal/pull/2251))
49
52
  - ISO-8859-1 and US-ASCII encodings are now separated as in MRI ([#2235](https://github.com/opal/opal/pull/2235))
50
53
  - `String#b` no longer modifies object strings in-place ([#2235](https://github.com/opal/opal/pull/2235))
54
+ - Parser::Builder::Default.check_lvar_name patch ([#2195](https://github.com/opal/opal/pull/2195))
51
55
 
52
56
  ### Changed
53
57
 
data/README.md CHANGED
@@ -116,7 +116,7 @@ your web applications.
116
116
  <html>
117
117
  <head>
118
118
  <meta charset="utf-8">
119
- <script src="https://cdn.opalrb.com/opal/current/opal.js" onload="Opal.load('opal')"></script>
119
+ <script src="https://cdn.opalrb.com/opal/current/opal.js"></script>
120
120
  <script src="https://cdn.opalrb.com/opal/current/opal-parser.js" onload="Opal.load('opal-parser')"></script>
121
121
 
122
122
  <script type="text/ruby">
data/Rakefile CHANGED
@@ -7,5 +7,6 @@ import 'tasks/building.rake'
7
7
  import 'tasks/linting.rake'
8
8
  import 'tasks/benchmarking.rake'
9
9
  import 'tasks/releasing.rake'
10
+ import 'tasks/performance.rake'
10
11
 
11
12
  task :default => [:rspec, :mspec, :minitest]
data/UNRELEASED.md CHANGED
@@ -1,50 +1,76 @@
1
+ ### Added
1
2
 
2
- - Support for multiple arguments in Hash#{merge, merge!, update} (#2187)
3
- - Support for Ruby 3.0 forward arguments: `def a(...) puts(...) end` (#2153)
4
- - Support for beginless and endless ranges: `(1..)`, `(..1)` (#2150)
5
- - Preliminary support for `**nil` argument - see #2240 to note limitations (#2152)
6
- - Support for `Random::Formatters` which add methods `#{hex,base64,urlsafe_base64,uuid,random_float,random_number,alphanumeric}` to `Random` and `SecureRandom` (#2218)
7
- - Basic support for ObjectSpace finalizers and ObjectSpace::WeakMap (#2247)
8
- - A more robust support for encodings (especially binary strings) (#2235)
9
- - Support for `"\x80"` syntax in String literals (#2235)
10
- - Added `String#+@`, `String#-@` (#2235)
11
- - Support for `begin <CODE> end while <CONDITION>` (#2255)
12
- - Added Hash#except and `Hash#except!` (#2243)
13
- - Parser 3.0: Implement pattern matching (as part of this `{Array,Hash,Struct}#{deconstruct,deconstruct_keys} methods were added)` (#2243)
14
- - [experimental] Reimplement Promise to make it bridged with JS native Promise, this new implementation can be used by requiring `promise/v2` (#2220)
15
-
3
+ - Add support for `retry` (#2264)
4
+ - Modernize Exceptions (#2264)
5
+ - Add `#cause`, `#backtrace_locations`, `#full_message` to `Exception`
6
+ - Normalize backtraces across platforms
7
+ - Add `Thread::Backtrace::Location`
8
+ - Output Exception#full_message on uncaught exceptions (#2269)
9
+ - TracePoint `:class` support (#2049)
10
+ - Implement the Flip-Flop operators (#2261)
11
+ - Add `JS[]` to access properties on the global object (#2259)
12
+ - Add `ENV.fetch` to the Nodejs implementation of `ENV` (#2259)
13
+ - Opal::Cache, an optional compiler cache (enabled by default) (#2242, #2278)
14
+ - Alias for gvars, alias on main (#2270)
15
+ - Support for GJS (GNOME's JavaScript runtime) runner (#2280)
16
+ - Scope variables support for `eval()` (#2256)
17
+ - Add support for `Kernel#binding` (#2256)
18
+ - A (mostly) correct support for refinements (#2256)
19
+ - [CI] Performance regression check (#2276, #2282)
20
+ - Add support for ECMAScript modules with an `--esm` CLI option (#2286)
21
+ - Implement `Regexp#names` and add named captures support (#2272)
22
+ - REPL improvements: (#2285)
23
+ - Colored output & history support
24
+ - `ls` to show available constants and variable
25
+ - Add `Method#===` as an alias to `Method#call`, works the same as `Proc#===` (#2305)
26
+ - Add `IO#gets` and `IO#read_proc` along with other supporting methods (#2309)
27
+ - Support `#gets` on most platforms, including browsers (via `prompt`)
28
+ - Move the REPL to a `--repl` CLI option of the main executable
29
+ - Completely refactor IO, now supporting
30
+ - Add a runner for MiniRacer (as `miniracer`)
31
+ - Support Windows on the Chrome runner
32
+ - Support Windows on the REPL
33
+ - Platforms an IO implementations should either set `IO#read_proc` or overwrite `IO#sysread`
34
+ - [experimental] Add support for JavaScript async/await (#2221)
35
+ - Enable the feature by adding a magic comment: `# await: true`
36
+ - The magic comment can be also used to mark specific method patterns to be awaited
37
+ (e.g. `# await: *_await, sleep` will make any method ending in `_await` or named `sleep` to be awaited)
38
+ - Add `Kernel#__await__` as a bridge to the `await` keyword (inspired by CoffeeScript await support)
39
+ - Require `opal/await` to get additional support
40
+ - Read more on the newly added documentation page
41
+ - Better interoperability between legacy Promise (v1) and native Promise (v2) (#2221)
42
+ - Add `PromiseV1` as an alias to the original (legacy) Promise class
43
+ - Add `#to_v1` and `#to_v2` to both classes
44
+ - `Promise#to_n` will convert it to a native Promise (v2)
16
45
 
17
46
  ### Fixed
18
47
 
19
- - Encoding lookup was working only with uppercase names, not giving any errors for wrong ones (#2181, #2183, #2190)
20
- - Fix `Number#to_i` with huge number (#2191)
21
- - Add regexp support to `String#start_with` (#2198)
22
- - `String#bytes` now works in strict mode (#2194)
23
- - Fix nested module inclusion (#2053)
24
- - SecureRandom is now cryptographically secure on most platforms (#2218, #2170)
25
- - Fix performance regression for `Array#unshift` on v8 > 7.1 (#2116)
26
- - String subclasses now call `#initialize` with multiple arguments correctly (with a limitation caused by the String immutability issue, that a source string must be the first argument and `#initialize` can't change its value) (#2238, #2185)
27
- - Number#step is moved to Numeric (#2100)
28
- - Fix class Class < superclass for invalid superclasses (#2123)
29
- - Fix `String#unpack("U*")` on binary strings with latin1 high characters, fix performance regression on that call (#2235, #2189, #2129, #2099, #2094, #2000, #2128)
30
- - Fix `String#to_json` output on some edge cases (#2235)
31
- - Rework class variables to support inheritance correctly (#2251)
32
- - ISO-8859-1 and US-ASCII encodings are now separated as in MRI (#2235)
33
- - `String#b` no longer modifies object strings in-place (#2235)
34
- - Parser::Builder::Default.check_lvar_name patch (#2195)
48
+ - Fixed multiple line `Regexp` literal to not generate invalid syntax as JavaScript (#1616)
49
+ - Fix `Kernel#{try,catch}` along with `UncaughtThrowError` (#2264)
50
+ - Update source-map-support to fix an off-by-one error (#2264)
51
+ - Source map: lines should start from 1, not 0 (#2273)
52
+ - Allow for multiple underscored args with the same name in strict mode (#2292)
53
+ - Show instance variables in `Kernel#inspect` (#2285)
54
+ - `0.digits` was returning an empty array in strict mode (#2301)
55
+ - Non Integer numbers were responding to `#digits` (#2301)
56
+ - Correctly delete hash members when dealing with boxed strings (#2306)
57
+ - Escape string components in interpolated strings (`dstrs`) correctly (#2308)
58
+ - Don't try to return the JS `debugger` statement, just return `nil` (#2307)
59
+ - Retain the `-` while stringifying `-0.0` (#2304)
60
+ - Fix super support for rest args and re-assignments with implicit arguments (#2315)
35
61
 
36
62
  ### Changed
37
63
 
38
- - `String#unpack`, `Array#pack`, `String#chars`, `String#length`, `Number#chr`, and (only partially) `String#+` are now encoding aware (#2235)
39
- - `String#inspect` now uses `\x` for binary stirngs (#2235)
40
- - `if RUBY_ENGINE == "opal"` and friends are now outputing less JS code (#2159, #1965)
41
- - `Array`: `to_a`, `slice`/`[]`, `uniq`, `*`, `difference`/`-`, `intersection`/`&`, `union`/`|`, flatten now return Array, not a subclass, as Ruby 3.0 does (#2237)
42
- - `Array`: `difference`, `intersection`, `union` now accept multiple arguments (#2237)
64
+ - Fast-track bad constant names passed to `Struct.new` (#2259)
65
+ - Renamed internal `super` related helpers,
66
+ `find_super_dispatcher` is now `find_super`, `find_iter_super_dispatcher` is now `find_block_super` (#2090)
67
+ - The `opal-repl` CLI now requires files to be passed with `--require` (or `-r`) instead of the bare filename (#2309)
43
68
 
44
- ### Deprecated
45
69
 
46
- - Stopped testing Opal on Ruby 2.5 since it reached EOL.
70
+ ### Deprecated
47
71
 
48
72
  ### Removed
49
73
 
50
- - Removed support for the outdated `c_lexer`, it was optional and didn't work for the last few releases of parser (#2235)
74
+ ### Internal
75
+
76
+ - Switch from jshint to ESLint (#2289)
data/docs/async.md ADDED
@@ -0,0 +1,109 @@
1
+ # Asynchronous code (PromiseV2 / async / await)
2
+
3
+ Please be aware that this functionality is marked as experimental and may change
4
+ in the future.
5
+
6
+ In order to disable the warnings that will be shown if you use those experimental
7
+ features, add the following line before requiring `promise/v2` or `await` and after
8
+ requiring `opal`.
9
+
10
+ ```ruby
11
+ `Opal.config.experimental_features_severity = 'ignore'`
12
+ ```
13
+
14
+ ## PromiseV2
15
+
16
+ In Opal 1.2 we introduced PromiseV2 which is to replace the default Promise in Opal 2.0
17
+ (which will become PromiseV1). Right now it's experimental, but the interface of PromiseV1
18
+ stay unchanged and will continue to be supported.
19
+
20
+ It is imperative that during the transition period you either `require 'promise/v1'` or
21
+ `require 'promise/v2'` and then use either `PromiseV1` or `PromiseV2`.
22
+
23
+ If you write library code it's imperative that you don't require the promise itself, but
24
+ detect if `PromiseV2` is defined and use the newer implementation, for instance using the
25
+ following code:
26
+
27
+ ```ruby
28
+ module MyLibrary
29
+ Promise = defined?(PromiseV2) ? PromiseV2 : ::Promise
30
+ end
31
+ ```
32
+
33
+ The difference between `PromiseV1` and `PromiseV2` is that `PromiseV1` is a pure-Ruby
34
+ implementation of a Promise, while `PromiseV2` is reusing the JavaScript `Promise`. Both are
35
+ incompatible with each other, but `PromiseV2` can be awaited (see below) and they translate
36
+ 1 to 1 to the JavaScript native `Promise` (they are bridged; you can directly return a
37
+ `Promise` from JavaScript API without a need to translate it). The other difference is that
38
+ `PromiseV2` always runs a `#then` block a tick later, while `PromiseV1` would could run it
39
+ instantaneously.
40
+
41
+ ## Async/await
42
+
43
+ In Opal 1.3 we implemented the CoffeeScript pattern of async/await. As of now, it's hidden
44
+ behind a magic comment, but this behavior may change in the future.
45
+
46
+ Example:
47
+
48
+ ```ruby
49
+ # await: true
50
+
51
+ require "await"
52
+
53
+ def wait_5_seconds
54
+ puts "Let's wait 5 seconds..."
55
+ sleep(5).await
56
+ puts "Done!"
57
+ end
58
+
59
+ wait_5_seconds.__await__
60
+ ```
61
+
62
+ It's important to understand what happens under the hood: every scope in which `#__await__` is
63
+ encountered will become async, which means that it will return a Promise that will resolve
64
+ to a value. This includes methods, blocks and the top scope. This means, that `#__await__` is
65
+ infectious and you need to remember to `#__await__` everything along the way, otherwise
66
+ a program will finish too early and the values may be incorrect.
67
+
68
+ [You can take a look at how we ported Minitest to support asynchronous tests.](https://github.com/opal/opal/pull/2221/commits/8383c7b45a94fe4628778f429508b9c08c8948b0) Take note, that
69
+ it was ported to use `#await` while the finally accepted version uses `#__await__`.
70
+
71
+ It is certainly correct to `#__await__` any value, including non-Promises, for instance
72
+ `5.__await__` will correctly resolve to `5` (except that it will make the scope an async
73
+ function, with all the limitations described above).
74
+
75
+ The `await` stdlib module includes a few useful functions, like async-aware `each_await`
76
+ function and `sleep` that doesn't block the thread. It also includes a method `#await`
77
+ which is an alias of `#itself` - it makes sense to auto-await that method.
78
+
79
+ This approach is certainly incompatible with what Ruby does, but due to a dynamic nature
80
+ of Ruby and a different model of JavaScript this was the least invasive way to catch up
81
+ with the latest JavaScript trends and support `Promise` heavy APIs and asynchronous code.
82
+
83
+ ## Auto-await
84
+
85
+ The magic comment also accepts a comma-separated list of methods to be automatically
86
+ awaited. An individual value can contain a wildcard character `*`. For instance,
87
+ those two blocks of code are equivalent:
88
+
89
+ ```ruby
90
+ # await: true
91
+
92
+ require "await"
93
+
94
+ [1,2,3].each_await do |i|
95
+ p i
96
+ sleep(i).__await__
97
+ end.__await__
98
+ ```
99
+
100
+ ```ruby
101
+ # await: sleep, *await*
102
+
103
+ require "await"
104
+
105
+ [1,2,3].each_await do |i|
106
+ p i
107
+ sleep i
108
+ end
109
+ ```
@@ -22,8 +22,6 @@ require 'roda'
22
22
  class App < Roda
23
23
  plugin :sprockets, precompile: %w(application.js),
24
24
  prefix: %w(app/),
25
- root: __dir__,
26
- public_path: 'public/',
27
25
  opal: true,
28
26
  debug: ENV['RACK_ENV'] != 'production'
29
27
  plugin :public
data/exe/opal CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  # Error codes are taken from /usr/include/sysexits.h
4
4
 
5
+ OriginalARGV = ARGV.dup
6
+
5
7
  require 'opal/cli_options'
6
8
  options = Opal::CLIOptions.new
7
9
  begin
data/exe/opal-repl CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'opal/repl'
3
+ ARGV << "--repl"
4
4
 
5
- repl = Opal::REPL.new.run ARGV.first
5
+ load __dir__+"/opal"
data/lib/opal/builder.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'opal/path_reader'
4
4
  require 'opal/paths'
5
5
  require 'opal/config'
6
+ require 'opal/cache'
6
7
  require 'set'
7
8
 
8
9
  module Opal
@@ -70,6 +71,7 @@ module Opal
70
71
  @prerequired ||= []
71
72
  @compiler_options ||= Opal::Config.compiler_options
72
73
  @missing_require_severity ||= Opal::Config.missing_require_severity
74
+ @cache ||= Opal.cache
73
75
 
74
76
  @processed = []
75
77
  end
@@ -133,7 +135,7 @@ module Opal
133
135
  attr_reader :processed
134
136
 
135
137
  attr_accessor :processors, :path_reader, :stubs, :prerequired, :preload,
136
- :compiler_options, :missing_require_severity
138
+ :compiler_options, :missing_require_severity, :cache
137
139
 
138
140
  private
139
141
 
@@ -165,6 +167,8 @@ module Opal
165
167
  "processors: #{processors.inspect}"
166
168
  )
167
169
 
170
+ options = options.merge(cache: cache)
171
+
168
172
  processor.new(source, rel_path, @compiler_options.merge(options))
169
173
  end
170
174
 
@@ -8,7 +8,8 @@ module Opal
8
8
  class Processor
9
9
  def initialize(source, filename, options = {})
10
10
  source += "\n" unless source.end_with?("\n")
11
- @source, @filename, @options = source, filename, options
11
+ @source, @filename, @options = source, filename, options.dup
12
+ @cache = @options.delete(:cache) { Opal.cache }
12
13
  @requires = []
13
14
  @required_trees = []
14
15
  end
@@ -79,13 +80,17 @@ module Opal
79
80
  end
80
81
 
81
82
  def compiled
82
- @compiled ||= begin
83
+ @compiled ||= Opal::Cache.fetch(@cache, cache_key) do
83
84
  compiler = compiler_for(@source, file: @filename)
84
85
  compiler.compile
85
86
  compiler
86
87
  end
87
88
  end
88
89
 
90
+ def cache_key
91
+ [self.class, @filename, @source, @options]
92
+ end
93
+
89
94
  def compiler_for(source, options = {})
90
95
  ::Opal::Compiler.new(source, @options.merge(options))
91
96
  end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'zlib'
5
+
6
+ module Opal
7
+ module Cache
8
+ class FileCache
9
+ def initialize(dir: nil, max_size: nil)
10
+ @dir = dir || self.class.find_dir
11
+ # Store at most 32MB of cache - de facto this 32MB is larger,
12
+ # as we don't account for inode size for instance. In fact, it's
13
+ # about 50M. Also we run this check before anything runs, so things
14
+ # may go up to 64M or even larger.
15
+ @max_size = max_size || 32 * 1024 * 1024
16
+
17
+ tidy_up_cache
18
+ end
19
+
20
+ def set(key, data)
21
+ file = cache_filename_for(key)
22
+
23
+ out = Marshal.dump(data)
24
+ out = Zlib.gzip(out, level: 9)
25
+ File.binwrite(file, out)
26
+ end
27
+
28
+ def get(key)
29
+ file = cache_filename_for(key)
30
+
31
+ if File.exist?(file)
32
+ FileUtils.touch(file)
33
+ out = File.binread(file)
34
+ out = Zlib.gunzip(out)
35
+ Marshal.load(out) # rubocop:disable Security/MarshalLoad
36
+ end
37
+ rescue Zlib::GzipFile::Error
38
+ nil
39
+ end
40
+
41
+ # Remove cache entries that overflow our cache limit... and which
42
+ # were used least recently.
43
+ private def tidy_up_cache
44
+ entries = Dir[@dir + '/*.rbm.gz']
45
+
46
+ size_sum = entries.map { |i| File.size(i) }.sum
47
+ return unless size_sum > @max_size
48
+
49
+ # First, we try to get the oldest files first.
50
+ # Then, what's more important, is that we try to get the least
51
+ # recently used files first. Filesystems with relatime or noatime
52
+ # will get this wrong, but it doesn't matter that much, because
53
+ # the previous sort got things "maybe right".
54
+ entries = entries.sort_by { |i| [File.mtime(i), File.atime(i)] }
55
+
56
+ entries.each do |i|
57
+ size_sum -= File.size(i)
58
+ File.unlink(i)
59
+
60
+ # We don't need to work this out anymore - we reached out goal.
61
+ break unless size_sum > @max_size
62
+ end
63
+ rescue Errno::ENOENT
64
+ # Do nothing, this comes from multithreading. We will tidy up at
65
+ # the next chance.
66
+ nil
67
+ end
68
+
69
+ # This complex piece of code tries to check if we can robustly mkdir_p a directory.
70
+ def self.dir_writable?(*paths)
71
+ dir = nil
72
+ paths = paths.reduce([]) do |a, b|
73
+ [*a, dir = a.last ? File.expand_path(b, a.last) : b]
74
+ end
75
+
76
+ File.exist?(paths.first) &&
77
+ paths.reverse.all? do |i|
78
+ !File.exist?(i) || (File.directory?(i) && File.writable?(i))
79
+ end
80
+
81
+ dir
82
+ end
83
+
84
+ def self.find_dir
85
+ @find_dir ||= case
86
+ # Try to write cache into a directory pointed by an environment variable if present
87
+ when dir = ENV['OPAL_CACHE_DIR']
88
+ FileUtils.mkdir_p(dir)
89
+ dir
90
+ # Otherwise, we write to the place where Opal is installed...
91
+ # I don't think it's a good location to store cache, so many things can go wrong.
92
+ # when dir = dir_writable?(Opal.gem_dir, '..', 'tmp', 'cache')
93
+ # FileUtils.mkdir_p(dir)
94
+ # FileUtils.chmod(0o700, dir)
95
+ # dir
96
+ # Otherwise, ~/.cache/opal...
97
+ when dir = dir_writable?(Dir.home, '.cache', 'opal')
98
+ FileUtils.mkdir_p(dir)
99
+ FileUtils.chmod(0o700, dir)
100
+ dir
101
+ # Only /tmp is writable... or isn't it?
102
+ when (dir = dir_writable?('/tmp', "opal-cache-#{ENV['USER']}")) && File.sticky?('/tmp')
103
+ FileUtils.mkdir_p(dir)
104
+ FileUtils.chmod(0o700, dir)
105
+ dir
106
+ # No way... we can't write anywhere...
107
+ else
108
+ warn "Couldn't find a writable path to store Opal cache. " \
109
+ 'Try setting OPAL_CACHE_DIR environment variable'
110
+ nil
111
+ end
112
+ end
113
+
114
+ private def cache_filename_for(key)
115
+ "#{@dir}/#{key}.rbm.gz"
116
+ end
117
+ end
118
+ end
119
+ end