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.
- checksums.yaml +4 -4
- data/.eslintrc.await.js +6 -0
- data/.eslintrc.js +34 -0
- data/.github/workflows/build.yml +8 -0
- data/.rubocop.yml +9 -0
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/Rakefile +1 -0
- data/UNRELEASED.md +64 -38
- data/docs/async.md +109 -0
- data/docs/roda-sprockets.md +0 -2
- data/exe/opal +2 -0
- data/exe/opal-repl +2 -2
- data/lib/opal/builder.rb +5 -1
- data/lib/opal/builder_processors.rb +7 -2
- data/lib/opal/cache/file_cache.rb +119 -0
- data/lib/opal/cache.rb +71 -0
- data/lib/opal/cli.rb +35 -1
- data/lib/opal/cli_options.rb +21 -0
- data/lib/opal/cli_runners/chrome.rb +21 -14
- data/lib/opal/cli_runners/chrome_cdp_interface.js +30285 -0
- data/lib/opal/cli_runners/{chrome.js → chrome_cdp_interface.rb} +27 -6
- data/lib/opal/cli_runners/compiler.rb +2 -1
- data/lib/opal/cli_runners/gjs.rb +27 -0
- data/lib/opal/cli_runners/mini_racer.rb +36 -0
- data/lib/opal/cli_runners/source-map-support-browser.js +276 -91
- data/lib/opal/cli_runners/source-map-support-node.js +276 -91
- data/lib/opal/cli_runners/source-map-support.js +60 -18
- data/lib/opal/cli_runners.rb +2 -0
- data/lib/opal/compiler.rb +99 -10
- data/lib/opal/fragment.rb +77 -14
- data/lib/opal/nodes/args/extract_kwrestarg.rb +6 -4
- data/lib/opal/nodes/args/extract_restarg.rb +10 -12
- data/lib/opal/nodes/args.rb +28 -0
- data/lib/opal/nodes/base.rb +29 -5
- data/lib/opal/nodes/call.rb +123 -2
- data/lib/opal/nodes/case.rb +7 -1
- data/lib/opal/nodes/class.rb +12 -2
- data/lib/opal/nodes/def.rb +3 -23
- data/lib/opal/nodes/definitions.rb +21 -4
- data/lib/opal/nodes/helpers.rb +2 -2
- data/lib/opal/nodes/if.rb +39 -9
- data/lib/opal/nodes/iter.rb +15 -3
- data/lib/opal/nodes/lambda.rb +3 -1
- data/lib/opal/nodes/literal.rb +13 -7
- data/lib/opal/nodes/logic.rb +2 -2
- data/lib/opal/nodes/module.rb +12 -2
- data/lib/opal/nodes/rescue.rb +59 -34
- data/lib/opal/nodes/scope.rb +88 -6
- data/lib/opal/nodes/super.rb +52 -25
- data/lib/opal/nodes/top.rb +13 -7
- data/lib/opal/nodes/while.rb +7 -1
- data/lib/opal/parser/patch.rb +2 -1
- data/lib/opal/repl.rb +137 -49
- data/lib/opal/rewriters/binary_operator_assignment.rb +10 -10
- data/lib/opal/rewriters/block_to_iter.rb +3 -3
- data/lib/opal/rewriters/for_rewriter.rb +7 -7
- data/lib/opal/rewriters/js_reserved_words.rb +5 -3
- data/lib/opal/source_map/file.rb +7 -4
- data/lib/opal/source_map/map.rb +17 -3
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +2 -2
- data/opal/corelib/binding.rb +46 -0
- data/opal/corelib/boolean.rb +54 -4
- data/opal/corelib/class.rb +2 -0
- data/opal/corelib/constants.rb +2 -2
- data/opal/corelib/error.rb +98 -12
- data/opal/corelib/io.rb +250 -38
- data/opal/corelib/kernel/format.rb +5 -2
- data/opal/corelib/kernel.rb +44 -23
- data/opal/corelib/main.rb +5 -0
- data/opal/corelib/method.rb +1 -0
- data/opal/corelib/module.rb +28 -0
- data/opal/corelib/number.rb +12 -1
- data/opal/corelib/random/seedrandom.js.rb +2 -2
- data/opal/corelib/regexp.rb +47 -3
- data/opal/corelib/runtime.js +152 -12
- data/opal/corelib/string/encoding.rb +17 -17
- data/opal/corelib/string.rb +2 -0
- data/opal/corelib/struct.rb +10 -3
- data/opal/corelib/trace_point.rb +57 -0
- data/opal/opal/full.rb +2 -0
- data/package.json +3 -2
- data/spec/filters/bugs/array.rb +0 -1
- data/spec/filters/bugs/basicobject.rb +0 -1
- data/spec/filters/bugs/binding.rb +27 -0
- data/spec/filters/bugs/enumerator.rb +132 -0
- data/spec/filters/bugs/exception.rb +70 -93
- data/spec/filters/bugs/float.rb +0 -1
- data/spec/filters/bugs/kernel.rb +3 -9
- data/spec/filters/bugs/language.rb +15 -58
- data/spec/filters/bugs/main.rb +16 -0
- data/spec/filters/bugs/matrix.rb +39 -0
- data/spec/filters/bugs/method.rb +0 -2
- data/spec/filters/bugs/module.rb +36 -79
- data/spec/filters/bugs/proc.rb +0 -1
- data/spec/filters/bugs/regexp.rb +0 -16
- data/spec/filters/bugs/trace_point.rb +12 -0
- data/spec/filters/bugs/warnings.rb +0 -4
- data/spec/filters/unsupported/freeze.rb +2 -0
- data/spec/filters/unsupported/privacy.rb +4 -0
- data/spec/lib/compiler_spec.rb +7 -1
- data/spec/lib/repl_spec.rb +4 -2
- data/spec/lib/source_map/file_spec.rb +1 -1
- data/spec/mspec-opal/formatters.rb +18 -4
- data/spec/mspec-opal/runner.rb +2 -2
- data/spec/opal/core/boolean_spec.rb +44 -0
- data/spec/opal/core/hash_spec.rb +8 -0
- data/spec/opal/core/number/to_s_spec.rb +11 -0
- data/spec/opal/stdlib/json/ext_spec.rb +3 -3
- data/spec/opal/stdlib/logger/logger_spec.rb +10 -1
- data/spec/ruby_specs +18 -0
- data/stdlib/await.rb +83 -0
- data/stdlib/base64.rb +4 -4
- data/stdlib/bigdecimal/bignumber.js.rb +4 -2
- data/stdlib/bigdecimal.rb +1 -0
- data/stdlib/gjs/io.rb +33 -0
- data/stdlib/gjs/kernel.rb +5 -0
- data/stdlib/gjs.rb +2 -0
- data/stdlib/js.rb +4 -0
- data/stdlib/json.rb +3 -3
- data/stdlib/logger.rb +1 -1
- data/stdlib/nashorn/file.rb +2 -0
- data/stdlib/nodejs/env.rb +7 -0
- data/stdlib/nodejs/file.rb +6 -41
- data/stdlib/nodejs/io.rb +21 -5
- data/stdlib/nodejs/js-yaml-3-6-1.js +2 -2
- data/stdlib/opal/miniracer.rb +6 -0
- data/stdlib/opal/platform.rb +4 -0
- data/stdlib/opal/repl_js.rb +5 -0
- data/stdlib/opal/replutils.rb +271 -0
- data/stdlib/opal-parser.rb +24 -11
- data/stdlib/opal-platform.rb +8 -0
- data/stdlib/promise/v2.rb +16 -4
- data/stdlib/promise.rb +14 -0
- data/stdlib/stringio.rb +13 -110
- data/stdlib/thread.rb +29 -0
- data/tasks/building.rake +10 -4
- data/tasks/linting-parse-eslint-results.js +39 -0
- data/tasks/linting.rake +38 -28
- data/tasks/performance/asciidoctor_test.rb.erb +6 -0
- data/tasks/performance/optimization_status.rb +77 -0
- data/tasks/performance.rake +149 -0
- data/tasks/testing.rake +9 -1
- data/test/nodejs/test_await.rb +169 -0
- data/test/opal/promisev2/test_error.rb +9 -3
- data/test/opal/unsupported_and_bugs.rb +5 -0
- data/vendored-minitest/minitest/benchmark.rb +9 -7
- data/vendored-minitest/minitest/test.rb +14 -12
- data/vendored-minitest/minitest.rb +19 -16
- data/yarn.lock +686 -117
- metadata +60 -23
- data/.jshintrc +0 -41
- data/spec/filters/unsupported/refinements.rb +0 -8
- data/vendored-minitest/minitest/hell.rb +0 -11
- data/vendored-minitest/minitest/parallel.rb +0 -65
- data/vendored-minitest/minitest/pride.rb +0 -4
- data/vendored-minitest/minitest/pride_plugin.rb +0 -142
- data/vendored-minitest/minitest/unit.rb +0 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf895e8ef63c69f90e6e099aa2f4c671649411893b980bdb03b55268da2e0a34
|
|
4
|
+
data.tar.gz: 5fc847637cea4a857bcc78ad1a491d5c898fdc051d779644f39e282e6c2aba66
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0eb66e6cb09d52075f7afd1ab8d10b67fdab302a503269f95b152440991b8d1f9bc3fc759e0265503350e6c4cd3c1453afca9d7d971bbdae46a367f15f5f8230
|
|
7
|
+
data.tar.gz: e0ce987c98345a13101dc3d9311ce1bb41435777edf41ab14aac96cf5821c1e6076236de25216c443fa806567af6f1a32de2f8d7d9e1e67e4ba165547c938e95
|
data/.eslintrc.await.js
ADDED
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
|
+
};
|
data/.github/workflows/build.yml
CHANGED
|
@@ -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"
|
|
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
data/UNRELEASED.md
CHANGED
|
@@ -1,50 +1,76 @@
|
|
|
1
|
+
### Added
|
|
1
2
|
|
|
2
|
-
-
|
|
3
|
-
-
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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
|
-
-
|
|
20
|
-
- Fix `
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
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
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
-
|
|
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
|
-
|
|
70
|
+
### Deprecated
|
|
47
71
|
|
|
48
72
|
### Removed
|
|
49
73
|
|
|
50
|
-
|
|
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
|
+
```
|
data/docs/roda-sprockets.md
CHANGED
data/exe/opal
CHANGED
data/exe/opal-repl
CHANGED
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 ||=
|
|
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
|