opal 1.4.1 → 1.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.eslintrc.js +5 -3
- data/.rubocop.yml +1 -0
- data/UNRELEASED.md +37 -2
- data/benchmark-ips/bm_js_symbols_vs_strings.rb +39 -14
- data/docs/releasing.md +10 -2
- data/lib/opal/ast/matcher.rb +77 -0
- data/lib/opal/cache.rb +1 -1
- data/lib/opal/cli_runners/applescript.rb +2 -0
- data/lib/opal/compiler.rb +18 -9
- data/lib/opal/nodes/call.rb +73 -28
- data/lib/opal/nodes/def.rb +31 -27
- data/lib/opal/nodes/definitions.rb +2 -0
- data/lib/opal/nodes/helpers.rb +4 -23
- data/lib/opal/nodes/if.rb +222 -0
- data/lib/opal/nodes/iter.rb +41 -37
- data/lib/opal/nodes/literal.rb +2 -2
- data/lib/opal/nodes/masgn.rb +15 -17
- data/lib/opal/nodes/node_with_args/shortcuts.rb +100 -0
- data/lib/opal/nodes/node_with_args.rb +1 -0
- data/lib/opal/nodes/top.rb +26 -10
- data/lib/opal/nodes.rb +0 -1
- data/lib/opal/parser/default_config.rb +3 -2
- data/lib/opal/repl.rb +1 -1
- data/lib/opal/rewriter.rb +13 -6
- data/lib/opal/rewriters/base.rb +12 -1
- data/lib/opal/rewriters/rubyspec/filters_rewriter.rb +1 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +23 -28
- data/opal/corelib/binding.rb +14 -4
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/hash.rb +2 -2
- data/opal/corelib/irb.rb +192 -0
- data/opal/corelib/math/polyfills.rb +127 -0
- data/opal/corelib/math.rb +14 -194
- data/opal/corelib/module.rb +23 -25
- data/opal/corelib/number.rb +63 -14
- data/opal/corelib/regexp.rb +2 -0
- data/opal/corelib/runtime.js +56 -20
- data/opal/corelib/string.rb +38 -59
- data/opal/corelib/time.rb +106 -68
- data/opal/opal/full.rb +0 -1
- data/opal/opal.rb +4 -1
- data/spec/filters/bugs/date.rb +0 -3
- data/spec/filters/bugs/datetime.rb +65 -0
- data/spec/filters/bugs/float.rb +0 -18
- data/spec/filters/bugs/hash.rb +0 -2
- data/spec/filters/bugs/language.rb +0 -3
- data/spec/filters/bugs/marshal.rb +0 -1
- data/spec/filters/bugs/string.rb +0 -30
- data/spec/filters/bugs/time.rb +18 -8
- data/spec/lib/cli_spec.rb +2 -2
- data/spec/lib/compiler_spec.rb +8 -8
- data/spec/lib/rewriters/base_spec.rb +1 -1
- data/spec/lib/rewriters/binary_operator_assignment_spec.rb +34 -59
- data/spec/lib/rewriters/block_to_iter_spec.rb +3 -6
- data/spec/lib/rewriters/dot_js_syntax_spec.rb +2 -5
- data/spec/lib/rewriters/for_rewriter_spec.rb +0 -1
- data/spec/lib/rewriters/forward_args_spec.rb +2 -3
- data/spec/lib/rewriters/js_reserved_words_spec.rb +2 -15
- data/spec/lib/rewriters/logical_operator_assignment_spec.rb +64 -89
- data/spec/lib/rewriters/numblocks_spec.rb +3 -5
- data/spec/lib/rewriters/opal_engine_check_spec.rb +2 -14
- data/spec/lib/rewriters/rubyspec/filters_rewriter_spec.rb +10 -2
- data/spec/opal/compiler/irb_spec.rb +4 -0
- data/spec/opal/core/language/super_spec.rb +26 -0
- data/spec/opal/core/regexp/assertions_spec.rb +19 -0
- data/spec/opal/core/string/to_proc_spec.rb +19 -0
- data/spec/ruby_specs +4 -0
- data/spec/support/rewriters_helper.rb +43 -23
- data/stdlib/date/date_time.rb +71 -0
- data/stdlib/date/formatters.rb +28 -0
- data/stdlib/date/infinity.rb +73 -0
- data/stdlib/date.rb +77 -214
- data/stdlib/opal/repl_js.rb +1 -1
- data/stdlib/{opal/replutils.rb → opal-replutils.rb} +3 -3
- data/stdlib/time.rb +39 -2
- data/stdlib/uri.rb +53 -0
- data/tasks/performance/asciidoctor_test.rb.erb +3 -1
- data/tasks/performance/optimization_status.rb +3 -2
- data/tasks/performance.rake +69 -35
- data/tasks/testing.rake +1 -0
- data/test/opal/test_uri.rb +35 -0
- data/yarn.lock +27 -5
- metadata +31 -18
- data/lib/opal/nodes/csend.rb +0 -24
- data/lib/opal/rewriters/explicit_writer_return.rb +0 -59
- data/spec/lib/rewriters/explicit_writer_return_spec.rb +0 -186
- data/stdlib/nodejs/irb.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ac3c5ae91f1f31b94c292f0aed320bdba76511abd34bb6dc45294c11958e802
|
4
|
+
data.tar.gz: 473ca0893f427b05c88c64805dc0f64668816b263b136863b903f5262aba7aef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ba1e5223c56695db351ede10b36872590b29701f4576c9a1962502686434548d855d71fdd147e5b34492bb70964db72bed3229cc552dfa139a2db747dd4caa1
|
7
|
+
data.tar.gz: 30e97d6c8293b918084145f15cd9f42d6495989405d97ebbccc414440c281a8297405f651560e8a65ea21dda191631d9bd20fda058d5693af1c25b8b61cc1e40
|
data/.eslintrc.js
CHANGED
@@ -24,12 +24,14 @@ module.exports = {
|
|
24
24
|
"no-control-regex": "off",
|
25
25
|
},
|
26
26
|
"globals": {
|
27
|
-
"Opal": "readonly",
|
28
|
-
"DataView": "readonly",
|
29
27
|
"ArrayBuffer": "readonly",
|
28
|
+
"DataView": "readonly",
|
30
29
|
"globalThis": "readonly",
|
31
|
-
"
|
30
|
+
"Opal": "readonly",
|
32
31
|
"Promise": "readonly",
|
32
|
+
"Proxy": "readonly",
|
33
|
+
"Reflect": "readonly",
|
34
|
+
"Uint8Array": "readonly",
|
33
35
|
"WeakRef": "readonly",
|
34
36
|
}
|
35
37
|
};
|
data/.rubocop.yml
CHANGED
data/UNRELEASED.md
CHANGED
@@ -1,8 +1,43 @@
|
|
1
|
-
|
1
|
+
### Added
|
2
|
+
|
3
|
+
- Introduce timezone support for Time (#2394)
|
4
|
+
- DateTime and Date refactor (#2398)
|
5
|
+
- Implement `Number#prev_float`/`#next_float` (#2404)
|
6
|
+
- Support `binding.irb` anywhere in the code, for both browsers and node (#2392)
|
7
|
+
- Added `URI.decode_www_form` (#2387)
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
|
11
|
+
- Move Math IE11-supporting polyfills to a separate file (#2395)
|
12
|
+
- String methods always return Strings even when overloaded (#2413)
|
13
|
+
- `alias` calls will not add the "old name" method to the list of stubs for method missing (#2414)
|
14
|
+
- Optimize writer/setter methods, up to 4% performance gain (#2402)
|
15
|
+
|
16
|
+
### Performance
|
17
|
+
|
18
|
+
- Improve performance of argument coertion, fast-track `Integer`, `String`, and calling the designed coertion method (#2383)
|
19
|
+
- Optimize `Array#[]=` by moving the implementation to JavaScript and inlining type checks (#2383)
|
20
|
+
- Optimize internal runtime passing of block-options (#2383)
|
21
|
+
- Compile `case` statements as `switch` whenever possible (#2411)
|
22
|
+
- Improve performance with optimized common method/iter implementation shortcuts (#2401)
|
23
|
+
|
2
24
|
### Fixed
|
25
|
+
|
26
|
+
- Fix `Regexp.new`, previously `\A` and `\z` didn't match beginning and end of input (#2079)
|
27
|
+
- Fix exception during `Hash#each` and `Hash#each_key` if keys get deleted during the loop (#2403)
|
28
|
+
- Fix defining multiple methods with the same block (#2397)
|
29
|
+
- A few edge cases of conditional calls combined with setters, e.g. `foo&.bar = 123` (#2402)
|
30
|
+
- Correct String#to_proc and method_missing compatibility (#2418)
|
31
|
+
- Exit REPL respecting the exit status number (#2396)
|
32
|
+
|
33
|
+
### Internal
|
34
|
+
|
35
|
+
- Rewriters refactor, fix interaction between cache and inverted runner (#2400)
|
36
|
+
|
37
|
+
<!--
|
38
|
+
### Internal
|
3
39
|
### Changed
|
4
40
|
### Added
|
5
41
|
### Removed
|
6
42
|
### Deprecated
|
7
|
-
### Internal
|
8
43
|
-->
|
@@ -1,33 +1,58 @@
|
|
1
1
|
Benchmark.ips do |x|
|
2
2
|
%x{
|
3
|
+
const c_foo = 'foo'
|
4
|
+
const v_foo = 'foo'
|
5
|
+
const cfoo = Symbol('foo')
|
6
|
+
var vfoo = Symbol('foo')
|
7
|
+
const cgfoo = Symbol.for('foo')
|
8
|
+
var vgfoo = Symbol.for('foo')
|
9
|
+
|
3
10
|
var o = {}
|
4
|
-
o[
|
5
|
-
o
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
o[cfoo] = 1
|
12
|
+
o[cgfoo] = 1
|
13
|
+
o[c_foo] = 1
|
14
|
+
o[vfoo] = 1
|
15
|
+
|
16
|
+
let a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0, a8 = 0
|
10
17
|
}
|
11
18
|
|
19
|
+
x.report('const string ref') do
|
20
|
+
`a1 += o[c_foo]`
|
21
|
+
end
|
22
|
+
|
23
|
+
x.report('var string ref') do
|
24
|
+
`a2 += o[v_foo]`
|
25
|
+
end
|
26
|
+
|
12
27
|
x.report('live global symbol') do
|
13
|
-
`
|
28
|
+
`a3 += o[Symbol.for('foo')]`
|
14
29
|
end
|
15
30
|
|
16
|
-
x.report('
|
17
|
-
`
|
31
|
+
x.report('const global symbol') do
|
32
|
+
`a4 += o[cgfoo]`
|
18
33
|
end
|
19
34
|
|
20
|
-
x.report('
|
21
|
-
`
|
35
|
+
x.report('var global symbol') do
|
36
|
+
`a5 += o[vgfoo]`
|
37
|
+
end
|
38
|
+
|
39
|
+
x.report('const symbol') do
|
40
|
+
`a6 += o[cfoo]`
|
41
|
+
end
|
42
|
+
|
43
|
+
x.report('var symbol') do
|
44
|
+
`a6 += o[vfoo]`
|
22
45
|
end
|
23
46
|
|
24
47
|
x.report('ident') do
|
25
|
-
`
|
48
|
+
`a7 += o.foo`
|
26
49
|
end
|
27
50
|
|
28
|
-
x.report('string') do
|
29
|
-
`
|
51
|
+
x.report('live string') do
|
52
|
+
`a8 += o['foo']`
|
30
53
|
end
|
31
54
|
|
55
|
+
x.time = 10
|
56
|
+
|
32
57
|
x.compare!
|
33
58
|
end
|
data/docs/releasing.md
CHANGED
@@ -10,8 +10,8 @@ _This guide is a work-in-progress._
|
|
10
10
|
## Updating the changelog
|
11
11
|
|
12
12
|
- Ensure all the unreleased changes are documented in UNRELEASED.md
|
13
|
-
- Run `bin/rake changelog VERSION=v1.2.3` specifying the version number you're about to release
|
14
|
-
- Empty UNRELEASED.md
|
13
|
+
- [skip for pre-releases] Run `bin/rake changelog VERSION=v1.2.3` specifying the version number you're about to release
|
14
|
+
- [skip for pre-releases] Empty UNRELEASED.md
|
15
15
|
|
16
16
|
## The commit
|
17
17
|
|
@@ -34,3 +34,11 @@ _This guide is a work-in-progress._
|
|
34
34
|
## Opal CDN
|
35
35
|
|
36
36
|
- Run `bin/release v1.2.3`
|
37
|
+
|
38
|
+
## Prepare for the next release
|
39
|
+
|
40
|
+
- Skip this step if releasing a stable release
|
41
|
+
- Create a new pull request that:
|
42
|
+
- Updates a version to `v1.x.0.dev` in both `lib/opal/version.rb` and `opal/corelib/constants.rb`
|
43
|
+
- Remember to merge that PR before merging anything else next once we decide to not release any more point releases from `master`.
|
44
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ast'
|
4
|
+
require 'parser/ast/node'
|
5
|
+
|
6
|
+
module Opal
|
7
|
+
module AST
|
8
|
+
class Matcher
|
9
|
+
def initialize(&block)
|
10
|
+
@root = instance_exec(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def s(type, *children)
|
14
|
+
Node.new(type, children)
|
15
|
+
end
|
16
|
+
|
17
|
+
def cap(capture)
|
18
|
+
Node.new(:capture, [capture])
|
19
|
+
end
|
20
|
+
|
21
|
+
def match(ast)
|
22
|
+
@captures = []
|
23
|
+
@root.match(ast, self) || (return false)
|
24
|
+
@captures
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"#<Opal::AST::Matcher: #{@root.inspect}>"
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :captures
|
32
|
+
|
33
|
+
Node = Struct.new(:type, :children) do
|
34
|
+
def match(ast, matcher)
|
35
|
+
return false if ast.nil?
|
36
|
+
|
37
|
+
ast_parts = [ast.type] + ast.children
|
38
|
+
self_parts = [type] + children
|
39
|
+
|
40
|
+
return false if ast_parts.length != self_parts.length
|
41
|
+
|
42
|
+
ast_parts.length.times.all? do |i|
|
43
|
+
ast_elem = ast_parts[i]
|
44
|
+
self_elem = self_parts[i]
|
45
|
+
|
46
|
+
if self_elem.is_a?(Node) && self_elem.type == :capture
|
47
|
+
capture = true
|
48
|
+
self_elem = self_elem.children.first
|
49
|
+
end
|
50
|
+
|
51
|
+
res = case self_elem
|
52
|
+
when Node
|
53
|
+
self_elem.match(ast_elem, matcher)
|
54
|
+
when Array
|
55
|
+
self_elem.include?(ast_elem)
|
56
|
+
when :*
|
57
|
+
true
|
58
|
+
else
|
59
|
+
self_elem == ast_elem
|
60
|
+
end
|
61
|
+
|
62
|
+
matcher.captures << ast_elem if capture
|
63
|
+
res
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def inspect
|
68
|
+
if type == :capture
|
69
|
+
"{#{children.first.inspect}}"
|
70
|
+
else
|
71
|
+
"s(#{type.inspect}, #{children.inspect[1..-2]})"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/opal/cache.rb
CHANGED
data/lib/opal/compiler.rb
CHANGED
@@ -217,9 +217,16 @@ module Opal
|
|
217
217
|
# Comments from the source code
|
218
218
|
attr_reader :comments
|
219
219
|
|
220
|
+
# Method calls made in this file
|
221
|
+
attr_reader :method_calls
|
222
|
+
|
220
223
|
# Magic comment flags extracted from the leading comments
|
221
224
|
attr_reader :magic_comments
|
222
225
|
|
226
|
+
# Set if some rewritter caused a dynamic cache result, meaning it's not
|
227
|
+
# fit to be cached
|
228
|
+
attr_accessor :dynamic_cache_result
|
229
|
+
|
223
230
|
def initialize(source, options = {})
|
224
231
|
@source = source
|
225
232
|
@indent = ''
|
@@ -227,8 +234,10 @@ module Opal
|
|
227
234
|
@options = options
|
228
235
|
@comments = Hash.new([])
|
229
236
|
@case_stmt = nil
|
237
|
+
@method_calls = Set.new
|
230
238
|
@option_values = {}
|
231
239
|
@magic_comments = {}
|
240
|
+
@dynamic_cache_result = false
|
232
241
|
end
|
233
242
|
|
234
243
|
# Compile some ruby code to a string.
|
@@ -260,9 +269,12 @@ module Opal
|
|
260
269
|
:main
|
261
270
|
end
|
262
271
|
|
263
|
-
@sexp =
|
264
|
-
|
265
|
-
|
272
|
+
@sexp = sexp.tap { |i| i.meta[:kind] = kind }
|
273
|
+
|
274
|
+
first_node = sexp.children.first if sexp.children.first.location
|
275
|
+
|
276
|
+
@comments = ::Parser::Source::Comment.associate_locations(first_node, comments)
|
277
|
+
@magic_comments = MagicComments.parse(first_node, comments)
|
266
278
|
@eof_content = EofContent.new(tokens, @source).eof
|
267
279
|
end
|
268
280
|
|
@@ -287,9 +299,8 @@ module Opal
|
|
287
299
|
)
|
288
300
|
end
|
289
301
|
|
290
|
-
|
291
|
-
|
292
|
-
@method_calls ||= Set.new
|
302
|
+
def record_method_call(mid)
|
303
|
+
@method_calls << mid
|
293
304
|
end
|
294
305
|
|
295
306
|
alias async_await_before_typecasting async_await
|
@@ -353,9 +364,7 @@ module Opal
|
|
353
364
|
@indent
|
354
365
|
end
|
355
366
|
|
356
|
-
# Create a new sexp using the given parts.
|
357
|
-
# returns an array, it must be used incase the internal structure
|
358
|
-
# of sexps does change.
|
367
|
+
# Create a new sexp using the given parts.
|
359
368
|
def s(type, *children)
|
360
369
|
::Opal::AST::Node.new(type, children)
|
361
370
|
end
|
data/lib/opal/nodes/call.rb
CHANGED
@@ -8,7 +8,7 @@ require 'opal/rewriters/break_finder'
|
|
8
8
|
module Opal
|
9
9
|
module Nodes
|
10
10
|
class CallNode < Base
|
11
|
-
handle :send
|
11
|
+
handle :send, :csend
|
12
12
|
|
13
13
|
attr_reader :recvr, :meth, :arglist, :iter
|
14
14
|
|
@@ -43,15 +43,19 @@ module Opal
|
|
43
43
|
# handle some methods specially
|
44
44
|
# some special methods need to skip compilation, so we pass the default as a block
|
45
45
|
handle_special do
|
46
|
-
compiler.
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
46
|
+
compiler.record_method_call meth
|
47
|
+
|
48
|
+
with_wrapper do
|
49
|
+
if using_eval?
|
50
|
+
# if trying to access an lvar in eval or irb mode
|
51
|
+
compile_eval_var
|
52
|
+
elsif using_irb?
|
53
|
+
# if trying to access an lvar in irb mode
|
54
|
+
compile_irb_var
|
55
|
+
else
|
56
|
+
default_compile
|
57
|
+
end
|
58
|
+
end
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
@@ -81,13 +85,18 @@ module Opal
|
|
81
85
|
# to a method body. This is some kind of protection from method calls
|
82
86
|
# like 'a(a {}) { 1 }'.
|
83
87
|
def invoke_using_send?
|
84
|
-
iter || splat?
|
88
|
+
iter || splat? || call_is_writer_that_needs_handling?
|
85
89
|
end
|
86
90
|
|
87
91
|
def invoke_using_refinement?
|
88
92
|
!scope.scope.collect_refinements_temps.empty?
|
89
93
|
end
|
90
94
|
|
95
|
+
# Is it a conditional send, ie. `foo&.bar`?
|
96
|
+
def csend?
|
97
|
+
@sexp.type == :csend
|
98
|
+
end
|
99
|
+
|
91
100
|
def default_compile
|
92
101
|
if auto_await?
|
93
102
|
push 'await '
|
@@ -143,17 +152,19 @@ module Opal
|
|
143
152
|
end
|
144
153
|
|
145
154
|
def compile_receiver
|
146
|
-
push recv(receiver_sexp)
|
155
|
+
push @conditional_recvr || recv(receiver_sexp)
|
147
156
|
end
|
148
157
|
|
149
158
|
def compile_method_name
|
150
159
|
push ", '#{meth}'"
|
151
160
|
end
|
152
161
|
|
153
|
-
def compile_arguments
|
154
|
-
push ', '
|
162
|
+
def compile_arguments(skip_comma = false)
|
163
|
+
push ', ' unless skip_comma
|
155
164
|
|
156
|
-
if
|
165
|
+
if @with_writer_temp
|
166
|
+
push @with_writer_temp
|
167
|
+
elsif splat?
|
157
168
|
push expr(arglist)
|
158
169
|
elsif arglist.children.empty?
|
159
170
|
push '[]'
|
@@ -197,16 +208,13 @@ module Opal
|
|
197
208
|
mid_to_jsid meth.to_s
|
198
209
|
end
|
199
210
|
|
200
|
-
def record_method?
|
201
|
-
true
|
202
|
-
end
|
203
|
-
|
204
211
|
# Used to generate the code to use this sexp as an ivar var reference
|
205
212
|
def compile_irb_var
|
206
213
|
with_temp do |tmp|
|
207
214
|
lvar = meth
|
208
215
|
call = s(:send, s(:self), meth.intern, s(:arglist))
|
209
|
-
|
216
|
+
ref = "(typeof #{lvar} !== 'undefined') ? #{lvar} : "
|
217
|
+
push "((#{tmp} = Opal.irb_vars.#{lvar}) == null ? ", ref, expr(call), " : #{tmp})"
|
210
218
|
end
|
211
219
|
end
|
212
220
|
|
@@ -255,7 +263,7 @@ module Opal
|
|
255
263
|
if invoke_using_refinement?
|
256
264
|
compile_default.call
|
257
265
|
elsif compiler.inline_operators?
|
258
|
-
compiler.
|
266
|
+
compiler.record_method_call operator
|
259
267
|
helper :"rb_#{name}"
|
260
268
|
lhs, rhs = expr(recvr), expr(arglist)
|
261
269
|
|
@@ -414,13 +422,8 @@ module Opal
|
|
414
422
|
|
415
423
|
scope.nesting
|
416
424
|
push "Opal.Binding.$new("
|
417
|
-
push " function($code
|
418
|
-
push "
|
419
|
-
push " return eval($code);"
|
420
|
-
push " }"
|
421
|
-
push " else {"
|
422
|
-
push " return eval($code + ' = $value');"
|
423
|
-
push " }"
|
425
|
+
push " function($code) {"
|
426
|
+
push " return eval($code);"
|
424
427
|
push " },"
|
425
428
|
push " ", scope.scope_locals.map(&:to_s).inspect, ","
|
426
429
|
push " ", scope.self, ","
|
@@ -450,6 +453,48 @@ module Opal
|
|
450
453
|
)
|
451
454
|
end
|
452
455
|
|
456
|
+
def with_wrapper(&block)
|
457
|
+
if csend? && !@conditional_recvr
|
458
|
+
handle_conditional_send do
|
459
|
+
with_wrapper(&block)
|
460
|
+
end
|
461
|
+
elsif call_is_writer_that_needs_handling?
|
462
|
+
handle_writer(&block)
|
463
|
+
else
|
464
|
+
yield
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def call_is_writer_that_needs_handling?
|
469
|
+
(expr? || recv?) && (meth.to_s =~ /^\w+=$/ || meth == :[]=)
|
470
|
+
end
|
471
|
+
|
472
|
+
# Handle safe-operator calls: foo&.bar / foo&.bar ||= baz / ...
|
473
|
+
def handle_conditional_send
|
474
|
+
# temporary variable that stores method receiver
|
475
|
+
receiver_temp = scope.new_temp
|
476
|
+
push "#{receiver_temp} = ", expr(recvr)
|
477
|
+
|
478
|
+
# execute the sexp only if the receiver isn't nil
|
479
|
+
push ", (#{receiver_temp} === nil || #{receiver_temp} == null) ? nil : "
|
480
|
+
@conditional_recvr = receiver_temp
|
481
|
+
yield
|
482
|
+
wrap '(', ')'
|
483
|
+
end
|
484
|
+
|
485
|
+
def handle_writer
|
486
|
+
with_temp do |temp|
|
487
|
+
push "(#{temp} = "
|
488
|
+
compile_arguments(true)
|
489
|
+
push ", "
|
490
|
+
@with_writer_temp = temp
|
491
|
+
yield
|
492
|
+
@with_writer_temp = false
|
493
|
+
push ", "
|
494
|
+
push "#{temp}[#{temp}.length - 1])"
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
453
498
|
class DependencyResolver
|
454
499
|
def initialize(compiler, sexp, missing_dynamic_require = nil)
|
455
500
|
@compiler = compiler
|
data/lib/opal/nodes/def.rb
CHANGED
@@ -10,6 +10,37 @@ module Opal
|
|
10
10
|
children :mid, :inline_args, :stmts
|
11
11
|
|
12
12
|
def compile
|
13
|
+
compile_body_or_shortcut
|
14
|
+
|
15
|
+
blockopts = []
|
16
|
+
|
17
|
+
blockopts << "$$arity: #{arity}"
|
18
|
+
|
19
|
+
if compiler.arity_check?
|
20
|
+
blockopts << "$$parameters: #{parameters_code}"
|
21
|
+
end
|
22
|
+
|
23
|
+
if compiler.parse_comments?
|
24
|
+
blockopts << "$$comments: #{comments_code}"
|
25
|
+
end
|
26
|
+
|
27
|
+
if compiler.enable_source_location?
|
28
|
+
blockopts << "$$source_location: #{source_location}"
|
29
|
+
end
|
30
|
+
|
31
|
+
if blockopts.length == 1
|
32
|
+
push ", #{arity}"
|
33
|
+
elsif blockopts.length > 1
|
34
|
+
push ', {', blockopts.join(', '), '}'
|
35
|
+
end
|
36
|
+
|
37
|
+
wrap_with_definition
|
38
|
+
|
39
|
+
scope.nesting if @define_nesting
|
40
|
+
scope.relative_access if @define_relative_access
|
41
|
+
end
|
42
|
+
|
43
|
+
def compile_body
|
13
44
|
inline_params = nil
|
14
45
|
scope_name = nil
|
15
46
|
|
@@ -51,33 +82,6 @@ module Opal
|
|
51
82
|
unshift "async "
|
52
83
|
end
|
53
84
|
line '}'
|
54
|
-
|
55
|
-
blockopts = []
|
56
|
-
|
57
|
-
blockopts << "$$arity: #{arity}"
|
58
|
-
|
59
|
-
if compiler.arity_check?
|
60
|
-
blockopts << "$$parameters: #{parameters_code}"
|
61
|
-
end
|
62
|
-
|
63
|
-
if compiler.parse_comments?
|
64
|
-
blockopts << "$$comments: #{comments_code}"
|
65
|
-
end
|
66
|
-
|
67
|
-
if compiler.enable_source_location?
|
68
|
-
blockopts << "$$source_location: #{source_location}"
|
69
|
-
end
|
70
|
-
|
71
|
-
if blockopts.length == 1
|
72
|
-
push ", #{arity}"
|
73
|
-
elsif blockopts.length > 1
|
74
|
-
push ', {', blockopts.join(', '), '}'
|
75
|
-
end
|
76
|
-
|
77
|
-
wrap_with_definition
|
78
|
-
|
79
|
-
scope.nesting if @define_nesting
|
80
|
-
scope.relative_access if @define_relative_access
|
81
85
|
end
|
82
86
|
|
83
87
|
def wrap_with_definition
|
@@ -31,6 +31,8 @@ module Opal
|
|
31
31
|
push '$alias_gvar(', new_name_str, ', ', old_name_str, ')'
|
32
32
|
when :dsym, :sym # This is a method alias: alias a b
|
33
33
|
helper :alias
|
34
|
+
compiler.record_method_call old_name.children.last if old_name.type == :sym
|
35
|
+
|
34
36
|
push "$alias(#{scope.self}, ", expr(new_name), ', ', expr(old_name), ')'
|
35
37
|
else # Nothing else is available, but just in case, drop an error
|
36
38
|
error "Opal doesn't know yet how to alias with #{new_name.type}"
|
data/lib/opal/nodes/helpers.rb
CHANGED
@@ -72,22 +72,22 @@ module Opal
|
|
72
72
|
case mid
|
73
73
|
when :==
|
74
74
|
helper :eqeq
|
75
|
-
compiler.
|
75
|
+
compiler.record_method_call mid
|
76
76
|
[fragment('$eqeq('), expr(receiver), fragment(', '), expr(args.first), fragment(')')]
|
77
77
|
when :===
|
78
78
|
helper :eqeqeq
|
79
|
-
compiler.
|
79
|
+
compiler.record_method_call mid
|
80
80
|
[fragment('$eqeqeq('), expr(receiver), fragment(', '), expr(args.first), fragment(')')]
|
81
81
|
when :!=
|
82
82
|
helper :neqeq
|
83
|
-
compiler.
|
83
|
+
compiler.record_method_call mid
|
84
84
|
[fragment('$neqeq('), expr(receiver), fragment(', '), expr(args.first), fragment(')')]
|
85
85
|
end
|
86
86
|
elsif args.count == 0
|
87
87
|
case mid
|
88
88
|
when :!
|
89
89
|
helper :not
|
90
|
-
compiler.
|
90
|
+
compiler.record_method_call mid
|
91
91
|
[fragment('$not('), expr(receiver), fragment(')')]
|
92
92
|
end
|
93
93
|
end
|
@@ -114,25 +114,6 @@ module Opal
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
|
-
|
118
|
-
# Usefule for safe-operator calls: foo&.bar / foo&.bar ||= baz / ...
|
119
|
-
#
|
120
|
-
# @param recvr [sexp_pushable] The receiver of the call that will be
|
121
|
-
# stored in a temporary variable
|
122
|
-
# @yields receiver_temp [String] the name of the temporary variable
|
123
|
-
# holding the ref to the original receiver, inside the block
|
124
|
-
# an expr() should be pushed.
|
125
|
-
#
|
126
|
-
def conditional_send(recvr)
|
127
|
-
# temporary variable that stores method receiver
|
128
|
-
receiver_temp = scope.new_temp
|
129
|
-
push "#{receiver_temp} = ", recvr
|
130
|
-
|
131
|
-
# execute the sexp only if the receiver isn't nil
|
132
|
-
push ", (#{receiver_temp} === nil || #{receiver_temp} == null) ? nil : "
|
133
|
-
yield receiver_temp
|
134
|
-
wrap '(', ')'
|
135
|
-
end
|
136
117
|
end
|
137
118
|
end
|
138
119
|
end
|