Skip to content

Commit 6041bea

Browse files
committed
[GR-18163] Fix --coverage together with multiple assignment using []=
PullRequest: truffleruby/2697
2 parents fd39199 + 7960a4c commit 6041bea

File tree

8 files changed

+75
-18
lines changed

8 files changed

+75
-18
lines changed

spec/ruby/language/variables_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,18 @@ module VariableSpecs
715715
x.should == [1, 2, 3, 4, 5]
716716
end
717717

718+
it "can be used to swap array elements" do
719+
a = [1, 2]
720+
a[0], a[1] = a[1], a[0]
721+
a.should == [2, 1]
722+
end
723+
724+
it "can be used to swap range of array elements" do
725+
a = [1, 2, 3, 4]
726+
a[0, 2], a[2, 2] = a[2, 2], a[0, 2]
727+
a.should == [3, 4, 1, 2]
728+
end
729+
718730
it "assigns RHS values to LHS constants" do
719731
module VariableSpecs
720732
MRHS_VALUES_1, MRHS_VALUES_2 = 1, 2

src/main/java/org/truffleruby/core/inlined/InlinedIndexSetNode.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,27 @@
1212
import com.oracle.truffle.api.dsl.Bind;
1313
import org.truffleruby.RubyLanguage;
1414
import org.truffleruby.core.array.ArrayWriteNormalizedNode;
15+
import org.truffleruby.core.array.AssignableNode;
1516
import org.truffleruby.core.array.RubyArray;
1617
import org.truffleruby.language.dispatch.RubyCallNodeParameters;
18+
import org.truffleruby.language.literal.NilLiteralNode;
1719
import org.truffleruby.language.methods.LookupMethodOnSelfNode;
1820

1921
import com.oracle.truffle.api.dsl.Cached;
2022
import com.oracle.truffle.api.dsl.Specialization;
2123
import com.oracle.truffle.api.frame.VirtualFrame;
2224
import com.oracle.truffle.api.profiles.ConditionProfile;
2325

24-
public abstract class InlinedIndexSetNode extends TernaryInlinedOperationNode {
26+
public abstract class InlinedIndexSetNode extends TernaryInlinedOperationNode implements AssignableNode {
2527

2628
protected static final String METHOD = "[]=";
2729

2830
public InlinedIndexSetNode(RubyLanguage language, RubyCallNodeParameters callNodeParameters) {
2931
super(language, callNodeParameters);
3032
}
3133

34+
protected abstract Object execute(VirtualFrame frame, Object receiver, Object index, Object value);
35+
3236
@Specialization(
3337
guards = {
3438
"lookupNode.lookupProtected(frame, array, METHOD) == coreMethods().ARRAY_INDEX_SET",
@@ -55,4 +59,16 @@ protected int normalize(RubyArray array, int index, ConditionProfile denormalize
5559
return index;
5660
}
5761

62+
@Override
63+
public void assign(VirtualFrame frame, Object value) {
64+
final Object receiver = getReceiver().execute(frame);
65+
final Object index = getOperand1().execute(frame);
66+
execute(frame, receiver, index, value);
67+
}
68+
69+
@Override
70+
public AssignableNode toAssignableNode() {
71+
assert getOperand2() instanceof NilLiteralNode && ((NilLiteralNode) getOperand2()).isImplicit() : getOperand2();
72+
return this;
73+
}
5874
}

src/main/java/org/truffleruby/language/dispatch/RubyCallNode.java

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import org.truffleruby.RubyLanguage;
1414
import org.truffleruby.core.array.ArrayToObjectArrayNode;
1515
import org.truffleruby.core.array.ArrayToObjectArrayNodeGen;
16-
import org.truffleruby.core.array.ArrayUtils;
1716
import org.truffleruby.core.array.AssignableNode;
1817
import org.truffleruby.core.cast.BooleanCastNode;
1918
import org.truffleruby.core.cast.BooleanCastNodeGen;
@@ -22,6 +21,7 @@
2221
import org.truffleruby.language.RubyBaseNode;
2322
import org.truffleruby.language.RubyContextSourceNode;
2423
import org.truffleruby.language.RubyNode;
24+
import org.truffleruby.language.literal.NilLiteralNode;
2525
import org.truffleruby.language.methods.BlockDefinitionNode;
2626
import org.truffleruby.language.methods.InternalMethod;
2727

@@ -88,7 +88,7 @@ public Object execute(VirtualFrame frame) {
8888
return nil;
8989
}
9090

91-
final Object[] executedArguments = executeArguments(frame, arguments.length);
91+
final Object[] executedArguments = executeArguments(frame);
9292

9393
final Object blockObject = executeBlock(frame);
9494

@@ -99,27 +99,28 @@ public Object execute(VirtualFrame frame) {
9999
}
100100

101101
@Override
102-
public void assign(VirtualFrame frame, Object extraArgument) {
102+
public void assign(VirtualFrame frame, Object value) {
103+
assert getLastArgumentNode() instanceof NilLiteralNode &&
104+
((NilLiteralNode) getLastArgumentNode()).isImplicit() : getLastArgumentNode();
105+
103106
final Object receiverObject = receiver.execute(frame);
104107
if (isSafeNavigation && nilProfile.profile(receiverObject == nil)) {
105108
return;
106109
}
107110

108-
final Object[] argumentsObjects;
109-
final Object blockObject;
110-
if (isSplatted) {
111-
final Object[] executedArguments = executeArguments(frame, arguments.length);
111+
final Object[] executedArguments = executeArguments(frame);
112112

113-
blockObject = executeBlock(frame);
113+
final Object blockObject = executeBlock(frame);
114114

115+
final Object[] argumentsObjects;
116+
if (isSplatted) {
115117
// The expansion of the splat is done after executing the block, for m(*args, &args.pop)
116-
argumentsObjects = ArrayUtils.append(splat(executedArguments), extraArgument);
118+
argumentsObjects = splat(executedArguments);
119+
assert argumentsObjects[argumentsObjects.length - 1] == nil;
120+
argumentsObjects[argumentsObjects.length - 1] = value;
117121
} else {
118-
final Object[] executedArguments = executeArguments(frame, arguments.length + 1);
119-
executedArguments[arguments.length] = extraArgument;
120-
121-
blockObject = executeBlock(frame);
122-
122+
assert executedArguments[arguments.length - 1] == nil;
123+
executedArguments[arguments.length - 1] = value;
123124
argumentsObjects = executedArguments;
124125
}
125126

@@ -152,8 +153,8 @@ private Object executeBlock(VirtualFrame frame) {
152153
}
153154

154155
@ExplodeLoop
155-
private Object[] executeArguments(VirtualFrame frame, int size) {
156-
final Object[] argumentsObjects = new Object[size];
156+
private Object[] executeArguments(VirtualFrame frame) {
157+
final Object[] argumentsObjects = new Object[arguments.length];
157158

158159
for (int i = 0; i < arguments.length; i++) {
159160
argumentsObjects[i] = arguments[i].execute(frame);
@@ -193,6 +194,10 @@ public boolean hasLiteralBlock() {
193194
return hasLiteralBlock;
194195
}
195196

197+
private RubyNode getLastArgumentNode() {
198+
return arguments[arguments.length - 1];
199+
}
200+
196201
@Override
197202
public AssignableNode toAssignableNode() {
198203
return this;

src/main/java/org/truffleruby/language/globals/WriteGlobalVariableNode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ public Object isDefined(VirtualFrame frame, RubyLanguage language, RubyContext c
8787
@Override
8888
public AssignableNode toAssignableNode() {
8989
// Cannot reassign a @NodeChild
90-
return WriteGlobalVariableNodeGen.create(name, null);
90+
final WriteGlobalVariableNode node = WriteGlobalVariableNodeGen.create(name, null);
91+
node.copySourceSection(this);
92+
return node;
9193
}
9294
}

src/main/java/org/truffleruby/parser/BodyTranslator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,9 @@ protected AssignableNode toAssignableNode(ParseNode node) {
17671767
return ((AssignableNode) translated).toAssignableNode();
17681768
} else if (node instanceof AttrAssignParseNode) {
17691769
final AttrAssignParseNode attrAssignParseNode = (AttrAssignParseNode) node;
1770+
// The AttrAssignParseNode does not have a value yet, add a sentinel nil,
1771+
// so the RubyCallNode is aware of the actual number of runtime arguments.
1772+
setRHS(attrAssignParseNode, NilImplicitParseNode.NIL);
17701773

17711774
final RubyNode translated = attrAssignParseNode.accept(this);
17721775
return ((AssignableNode) translated).toAssignableNode();

src/main/java/org/truffleruby/parser/ast/FixnumParseNode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,9 @@ public void setValue(long value) {
7171
public List<ParseNode> childNodes() {
7272
return EMPTY_LIST;
7373
}
74+
75+
@Override
76+
protected String toStringInternal() {
77+
return "value=" + value;
78+
}
7479
}

src/main/java/org/truffleruby/parser/lexer/RubyLexer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3779,4 +3779,13 @@ private boolean isFirstCodepointUppercase(Rope rope) {
37793779
return ropeEncoding.isUpper(firstCharacter);
37803780
}
37813781
}
3782+
3783+
public String getLocation() {
3784+
return getFile() + ":" + ruby_sourceline;
3785+
}
3786+
3787+
@Override
3788+
public String toString() {
3789+
return super.toString() + " @ " + getLocation();
3790+
}
37823791
}

src/main/java/org/truffleruby/parser/parser/ParserSupport.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ public void reset() {
191191
inDefinition = false;
192192
}
193193

194+
@Override
195+
public String toString() {
196+
return super.toString() + " @ " + lexer.getLocation();
197+
}
198+
194199
public StaticScope getCurrentScope() {
195200
return currentScope;
196201
}

0 commit comments

Comments
 (0)