Skip to content

Commit 920f770

Browse files
committed
Fix attribute and reference operator assignment and ignore methods visibility when receiver is self
1 parent 9c542a2 commit 920f770

13 files changed

+633
-45
lines changed

spec/ruby/language/assignments_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@
1818
ScratchPad.recorded.should == [:evaluated]
1919
@a.b.should == 3
2020
end
21+
22+
it 'ignores method visibility when receiver is self' do
23+
klass_with_private_methods = Class.new do
24+
def initialize(n) @a = n end
25+
def public_method(n); self.a += n end
26+
private
27+
def a; @a end
28+
def a=(n) @a = n; 42 end
29+
end
30+
31+
a = klass_with_private_methods.new(0)
32+
a.public_method(2).should == 2
33+
end
2134
end
2235

2336
describe 'using a #[]' do
@@ -30,6 +43,19 @@
3043
ScratchPad.recorded.should == [:evaluated]
3144
a[:k].should == 3
3245
end
46+
47+
it 'ignores method visibility when receiver is self' do
48+
klass_with_private_methods = Class.new do
49+
def initialize(h) @a = h end
50+
def public_method(k, n); self[k] += n end
51+
private
52+
def [](k) @a[k] end
53+
def []=(k, v) @a[k] = v; 42 end
54+
end
55+
56+
a = klass_with_private_methods.new(k: 0)
57+
a.public_method(:k, 2).should == 2
58+
end
3359
end
3460

3561
describe 'using compounded constants' do

spec/ruby/language/optional_assignments_spec.rb

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -132,27 +132,17 @@ def @a.b=(x)
132132
(@a.b ||= 20).should == 10
133133
end
134134

135-
it 'works when writer is private' do
136-
klass = Class.new do
137-
def t
138-
self.b = false
139-
(self.b ||= 10).should == 10
140-
(self.b ||= 20).should == 10
141-
end
142-
143-
def b
144-
@b
145-
end
146-
147-
def b=(x)
148-
@b = x
149-
:v
150-
end
151-
152-
private :b=
135+
it 'ignores method visibility when receiver is self' do
136+
klass_with_private_methods = Class.new do
137+
def initialize(v) @a = v end
138+
def public_method(v); self.a ||= v end
139+
private
140+
def a; @a end
141+
def a=(v) @a = v; 42 end
153142
end
154143

155-
klass.new.t
144+
a = klass_with_private_methods.new(false)
145+
a.public_method(10).should == 10
156146
end
157147
end
158148

@@ -225,6 +215,19 @@ def []=(x, y, value)
225215
ScratchPad.recorded.should == [:evaluated]
226216
@a[:k].should == 2
227217
end
218+
219+
it 'ignores method visibility when receiver is self' do
220+
klass_with_private_methods = Class.new do
221+
def initialize(h) @a = h end
222+
def public_method(k, v); self[k] ||= v end
223+
private
224+
def [](k) @a[k] end
225+
def []=(k, v) @a[k] = v; 42 end
226+
end
227+
228+
a = klass_with_private_methods.new(k: false)
229+
a.public_method(:k, 10).should == 10
230+
end
228231
end
229232
end
230233

@@ -326,6 +329,19 @@ def []=(x, y, value)
326329
ScratchPad.recorded.should == [:evaluated]
327330
@a.b.should == 20
328331
end
332+
333+
it 'ignores method visibility when receiver is self' do
334+
klass_with_private_methods = Class.new do
335+
def initialize(v) @a = v end
336+
def public_method(v); self.a &&= v end
337+
private
338+
def a; @a end
339+
def a=(v) @a = v; 42 end
340+
end
341+
342+
a = klass_with_private_methods.new(true)
343+
a.public_method(10).should == 10
344+
end
329345
end
330346

331347
describe 'using a #[]' do
@@ -446,6 +462,19 @@ def []=(x, y, value)
446462
@b[:k] = 17
447463
(@b[:k] += 12).should == 29
448464
end
465+
466+
it 'ignores method visibility when receiver is self' do
467+
klass_with_private_methods = Class.new do
468+
def initialize(h) @a = h end
469+
def public_method(k, v); self[k] &&= v end
470+
private
471+
def [](k) @a[k] end
472+
def []=(k, v) @a[k] = v; 42 end
473+
end
474+
475+
a = klass_with_private_methods.new(k: true)
476+
a.public_method(:k, 10).should == 10
477+
end
449478
end
450479
end
451480

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
fails:Assignments using += using compounded constants causes side-effects of the module part to be applied only once (when assigns)
2+
fails:Assignments using += using an accessor ignores method visibility when receiver is self
3+
fails:Assignments using += using a #[] ignores method visibility when receiver is self
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
fails:Optional constant assignment with &&= causes side-effects of the module part to be applied only once (when assigns)
2+
fails:Optional variable assignments using ||= using a #[] ignores method visibility when receiver is self
3+
fails:Optional variable assignments using &&= using a #[] ignores method visibility when receiver is self
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
subject: "&&="
2+
description: "Assign attribute with explicit self receiver (self.b &&= c)"
3+
notes: >
4+
`a.b &&= c` is translated into `a.b && a.b = c` in the following way:
5+
6+
```ruby
7+
value_0 = a # execute receiver only once
8+
value_0.b && value_0.b=(c)
9+
```
10+
focused_on_node: "org.truffleruby.language.defined.DefinedWrapperNode"
11+
ruby: |
12+
self.foo &&= 42
13+
ast: |
14+
DefinedWrapperNode
15+
attributes:
16+
definition = assignment
17+
flags = 1
18+
children:
19+
child =
20+
AndNodeGen
21+
attributes:
22+
flags = 0
23+
children:
24+
left =
25+
RubyCallNode
26+
attributes:
27+
descriptor = NoKeywordArgumentsDescriptor
28+
dispatchConfig = PRIVATE
29+
emptyKeywordsProfile = false
30+
flags = 0
31+
isAttrAssign = false
32+
isSafeNavigation = false
33+
isSplatted = false
34+
isVCall = false
35+
lastArgIsNotHashProfile = false
36+
methodName = "foo"
37+
notEmptyKeywordsProfile = false
38+
notRuby2KeywordsHashProfile = false
39+
children:
40+
receiver =
41+
SelfNode
42+
attributes:
43+
flags = 0
44+
right =
45+
RubyCallNode
46+
attributes:
47+
descriptor = NoKeywordArgumentsDescriptor
48+
dispatchConfig = PRIVATE
49+
emptyKeywordsProfile = false
50+
flags = 0
51+
isAttrAssign = true
52+
isSafeNavigation = false
53+
isSplatted = false
54+
isVCall = false
55+
lastArgIsNotHashProfile = false
56+
methodName = "foo="
57+
notEmptyKeywordsProfile = false
58+
notRuby2KeywordsHashProfile = false
59+
children:
60+
arguments = [
61+
IntegerFixnumLiteralNode
62+
attributes:
63+
flags = 0
64+
value = 42
65+
]
66+
receiver =
67+
SelfNode
68+
attributes:
69+
flags = 0
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
subject: "&&="
2+
description: "Assign an referenced element with explicit self receiver (self[a] &&= b)"
3+
notes: >
4+
`a[b] &&= c` is translated into `a[b] && a[b] = c` in the following way:
5+
6+
```ruby
7+
temp_arg1 = b
8+
temp_receiver = a
9+
10+
temp_receiver[temp_arg1] && temp_receiver[temp_arg1] = c
11+
```
12+
yarp_specific: true # don't put self into a local variable and fixed call node and set ignoreVisibility=true
13+
focused_on_node: "org.truffleruby.language.control.SequenceNode"
14+
ruby: |
15+
self[42] &&= 100500
16+
ast: |
17+
SequenceNode
18+
attributes:
19+
flags = 12
20+
children:
21+
body = [
22+
WriteLocalVariableNode
23+
attributes:
24+
flags = 0
25+
frameSlot = 0 # (self)
26+
children:
27+
valueNode =
28+
ProfileArgumentNodeGen
29+
attributes:
30+
flags = 0
31+
children:
32+
childNode_ =
33+
ReadSelfNode
34+
attributes:
35+
flags = 0
36+
WriteLocalVariableNode
37+
attributes:
38+
flags = 0
39+
frameSlot = 2 # %value_0
40+
children:
41+
valueNode =
42+
IntegerFixnumLiteralNode
43+
attributes:
44+
flags = 0
45+
value = 42
46+
AndNodeGen
47+
attributes:
48+
flags = 0
49+
children:
50+
left =
51+
InlinedIndexGetNodeGen
52+
attributes:
53+
assumptions = [Assumption(valid, name=set_trace_func is not used)]
54+
flags = 0
55+
parameters = RubyCallNodeParameters{methodName='[]', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=false}
56+
children:
57+
leftNode_ =
58+
SelfNode
59+
attributes:
60+
flags = 0
61+
rightNode_ =
62+
ReadLocalVariableNode
63+
attributes:
64+
flags = 0
65+
frameSlot = 2 # %value_0
66+
type = FRAME_LOCAL
67+
right =
68+
InlinedIndexSetNodeGen
69+
attributes:
70+
assumptions = [Assumption(valid, name=set_trace_func is not used)]
71+
flags = 0
72+
parameters = RubyCallNodeParameters{methodName='[]=', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=true, isVCall=false, isSafeNavigation=false, isAttrAssign=true}
73+
children:
74+
operand1Node_ =
75+
ReadLocalVariableNode
76+
attributes:
77+
flags = 0
78+
frameSlot = 2 # %value_0
79+
type = FRAME_LOCAL
80+
operand2Node_ =
81+
IntegerFixnumLiteralNode
82+
attributes:
83+
flags = 0
84+
value = 100500
85+
receiver_ =
86+
SelfNode
87+
attributes:
88+
flags = 0
89+
]
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
subject: "+="
2+
description: "Assign attribute with explicit self receiver (self.b += c)"
3+
notes: >
4+
`a.b += c` is translated into `a.b = a.b + c` in the following way:
5+
6+
```ruby
7+
temp_receiver = a
8+
temp_receiver.b = temp_receiver.b + c
9+
```
10+
yarp_specific: true # fixed assigning method call node and set `isAttrAssign = true`
11+
focused_on_node: "org.truffleruby.language.control.SequenceNode"
12+
ruby: |
13+
self.foo += 42
14+
ast: |
15+
SequenceNode
16+
attributes:
17+
flags = 12
18+
children:
19+
body = [
20+
WriteLocalVariableNode
21+
attributes:
22+
flags = 0
23+
frameSlot = 0 # (self)
24+
children:
25+
valueNode =
26+
ProfileArgumentNodeGen
27+
attributes:
28+
flags = 0
29+
children:
30+
childNode_ =
31+
ReadSelfNode
32+
attributes:
33+
flags = 0
34+
RubyCallNode
35+
attributes:
36+
descriptor = NoKeywordArgumentsDescriptor
37+
dispatchConfig = PRIVATE
38+
emptyKeywordsProfile = false
39+
flags = 0
40+
isAttrAssign = true
41+
isSafeNavigation = false
42+
isSplatted = false
43+
isVCall = false
44+
lastArgIsNotHashProfile = false
45+
methodName = "foo="
46+
notEmptyKeywordsProfile = false
47+
notRuby2KeywordsHashProfile = false
48+
children:
49+
arguments = [
50+
InlinedAddNodeGen
51+
attributes:
52+
assumptions = [Assumption(valid, name=set_trace_func is not used), Assumption(valid, name=inlined Integer#+), Assumption(valid, name=inlined Float#+)]
53+
flags = 0
54+
parameters = RubyCallNodeParameters{methodName='+', descriptor=NoKeywordArgumentsDescriptor, isSplatted=false, ignoreVisibility=false, isVCall=false, isSafeNavigation=false, isAttrAssign=false}
55+
children:
56+
leftNode_ =
57+
RubyCallNode
58+
attributes:
59+
descriptor = NoKeywordArgumentsDescriptor
60+
dispatchConfig = PRIVATE
61+
emptyKeywordsProfile = false
62+
flags = 0
63+
isAttrAssign = false
64+
isSafeNavigation = false
65+
isSplatted = false
66+
isVCall = false
67+
lastArgIsNotHashProfile = false
68+
methodName = "foo"
69+
notEmptyKeywordsProfile = false
70+
notRuby2KeywordsHashProfile = false
71+
children:
72+
receiver =
73+
SelfNode
74+
attributes:
75+
flags = 0
76+
rightNode_ =
77+
IntegerFixnumLiteralNode
78+
attributes:
79+
flags = 0
80+
value = 42
81+
]
82+
receiver =
83+
SelfNode
84+
attributes:
85+
flags = 0
86+
]

0 commit comments

Comments
 (0)