@@ -586,6 +586,9 @@ public RubyNode visitBreakNode(Nodes.BreakNode node) {
586
586
587
587
@ Override
588
588
public RubyNode visitCallAndWriteNode (Nodes .CallAndWriteNode node ) {
589
+ // `a.b &&= value` is translated into `a.b || a.b = value`
590
+ // receiver (a) should be executed only once that's why it's cached into a local variable
591
+
589
592
assert node .receiver != null ; // without receiver `a &&= b` leads to Nodes.LocalVariableAndWriteNode
590
593
591
594
final var receiverExpression = new YARPExecutedOnceExpression ("value" , node .receiver , this );
@@ -787,6 +790,9 @@ private boolean isAttrAssign(String methodName) {
787
790
788
791
@ Override
789
792
public RubyNode visitCallOperatorWriteNode (Nodes .CallOperatorWriteNode node ) {
793
+ // e.g. `a.b += value` is translated into `a.b = a.b + value`,
794
+ // receiver (a) should be executed only once - that's why it's cached in a local variable
795
+
790
796
assert node .receiver != null ; // without receiver `a += b` leads to Nodes.LocalVariableOperatorWriteNode
791
797
792
798
final var receiverExpression = new YARPExecutedOnceExpression ("value" , node .receiver , this );
@@ -817,6 +823,9 @@ public RubyNode visitCallOperatorWriteNode(Nodes.CallOperatorWriteNode node) {
817
823
818
824
@ Override
819
825
public RubyNode visitCallOrWriteNode (Nodes .CallOrWriteNode node ) {
826
+ // `a.b ||= value` is translated into `a.b || a.b = value`
827
+ // receiver (a) should be executed only once that's why it's cached into a local variable
828
+
820
829
assert node .receiver != null ; // without receiver `a ||= b` leads to Nodes.LocalVariableOrWriteNode
821
830
822
831
final var receiverExpression = new YARPExecutedOnceExpression ("value" , node .receiver , this );
@@ -1019,6 +1028,9 @@ public RubyNode visitClassNode(Nodes.ClassNode node) {
1019
1028
1020
1029
@ Override
1021
1030
public RubyNode visitClassVariableAndWriteNode (Nodes .ClassVariableAndWriteNode node ) {
1031
+ // `@@a &&= value` is translated into @@a && @@a = value`
1032
+ // don't check whether variable is defined so exception will be raised otherwise
1033
+
1022
1034
int startOffset = node .startOffset ;
1023
1035
int length = node .length ;
1024
1036
@@ -1032,6 +1044,9 @@ public RubyNode visitClassVariableAndWriteNode(Nodes.ClassVariableAndWriteNode n
1032
1044
1033
1045
@ Override
1034
1046
public RubyNode visitClassVariableOperatorWriteNode (Nodes .ClassVariableOperatorWriteNode node ) {
1047
+ // e.g. `@@a += value` is translated into @@a = @@a + value`
1048
+ // don't check whether variable is initialized so exception will be raised otherwise
1049
+
1035
1050
int startOffset = node .startOffset ;
1036
1051
int length = node .length ;
1037
1052
var readNode = new Nodes .ClassVariableReadNode (node .name , startOffset , length );
@@ -1042,13 +1057,17 @@ public RubyNode visitClassVariableOperatorWriteNode(Nodes.ClassVariableOperatorW
1042
1057
1043
1058
@ Override
1044
1059
public RubyNode visitClassVariableOrWriteNode (Nodes .ClassVariableOrWriteNode node ) {
1045
- // `@@a ||= value` is translated into `(defined?(@@a) && @@a) || @@a = value`
1060
+ // `@@a ||= value` is translated into (defined?(@@a) && @@a) || @@a = value`
1061
+ // so we check whether variable is defined and no exception will be raised otherwise
1062
+
1046
1063
int startOffset = node .startOffset ;
1047
1064
int length = node .length ;
1065
+
1048
1066
var writeNode = new Nodes .ClassVariableWriteNode (node .name , node .value , startOffset , length ).accept (this );
1049
1067
var readNode = new Nodes .ClassVariableReadNode (node .name , startOffset , length ).accept (this );
1050
- var definedCheck = AndNodeGen .create (new DefinedNode (readNode ), readNode );
1051
- final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (definedCheck , writeNode );
1068
+ var andNode = AndNodeGen .create (new DefinedNode (readNode ), readNode );
1069
+
1070
+ final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (andNode , writeNode );
1052
1071
return assignPositionAndFlags (node , rubyNode );
1053
1072
}
1054
1073
@@ -1084,6 +1103,9 @@ public RubyNode visitClassVariableTargetNode(Nodes.ClassVariableTargetNode node)
1084
1103
1085
1104
@ Override
1086
1105
public RubyNode visitConstantAndWriteNode (Nodes .ConstantAndWriteNode node ) {
1106
+ // `A &&= value` is translated into `A && A = value`
1107
+ // don't check whether constant is defined and so exception will be raised otherwise
1108
+
1087
1109
int startOffset = node .startOffset ;
1088
1110
int length = node .length ;
1089
1111
@@ -1097,6 +1119,9 @@ public RubyNode visitConstantAndWriteNode(Nodes.ConstantAndWriteNode node) {
1097
1119
1098
1120
@ Override
1099
1121
public RubyNode visitConstantOperatorWriteNode (Nodes .ConstantOperatorWriteNode node ) {
1122
+ // e.g. `A += value` is translated into A = A + value`
1123
+ // don't check whether constant is initialized so warnings will be emitted otherwise
1124
+
1100
1125
int startOffset = node .startOffset ;
1101
1126
int length = node .length ;
1102
1127
@@ -1112,20 +1137,24 @@ public RubyNode visitConstantOperatorWriteNode(Nodes.ConstantOperatorWriteNode n
1112
1137
@ Override
1113
1138
public RubyNode visitConstantOrWriteNode (Nodes .ConstantOrWriteNode node ) {
1114
1139
// `A ||= value` is translated into `(defined?(A) && A) || A = value`
1140
+ // so we check whether constant is defined and no exception will be raised otherwise
1115
1141
1116
1142
int startOffset = node .startOffset ;
1117
1143
int length = node .length ;
1118
1144
1119
1145
var writeNode = new Nodes .ConstantWriteNode (node .name , node .value , startOffset , length ).accept (this );
1120
1146
var readNode = new Nodes .ConstantReadNode (node .name , startOffset , length ).accept (this );
1121
- var definedCheck = AndNodeGen .create (new DefinedNode (readNode ), readNode );
1147
+ var andNode = AndNodeGen .create (new DefinedNode (readNode ), readNode );
1122
1148
1123
- final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (definedCheck , writeNode );
1149
+ final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (andNode , writeNode );
1124
1150
return assignPositionAndFlags (node , rubyNode );
1125
1151
}
1126
1152
1127
1153
@ Override
1128
1154
public RubyNode visitConstantPathAndWriteNode (Nodes .ConstantPathAndWriteNode node ) {
1155
+ // `A::B &&= value` is translated into `A::B && A::B = value`
1156
+ // don't check whether constant is defined and so exception will be raised otherwise
1157
+
1129
1158
var value = node .value .accept (this );
1130
1159
1131
1160
var readNode = (ReadConstantNode ) node .target .accept (this );
@@ -1161,6 +1190,9 @@ public RubyNode visitConstantPathNode(Nodes.ConstantPathNode node) {
1161
1190
1162
1191
@ Override
1163
1192
public RubyNode visitConstantPathOperatorWriteNode (Nodes .ConstantPathOperatorWriteNode node ) {
1193
+ // e.g. `A::B += value` is translated into A::B = A::B + value`
1194
+ // don't check whether constant is initialized so warnings will be emitted otherwise
1195
+
1164
1196
int startOffset = node .startOffset ;
1165
1197
int length = node .length ;
1166
1198
@@ -1176,6 +1208,12 @@ public RubyNode visitConstantPathOperatorWriteNode(Nodes.ConstantPathOperatorWri
1176
1208
1177
1209
@ Override
1178
1210
public RubyNode visitConstantPathOrWriteNode (Nodes .ConstantPathOrWriteNode node ) {
1211
+ // `A::B ||= value` is translated into `(defined?(A::B) && A::B) || A::B = value`
1212
+ // check whether constant is defined so no exception will be raised otherwise
1213
+
1214
+ // The defined? check is implemented in OrAssignConstantNodeGen and isn't straightforward
1215
+ // because of constants autoloading.
1216
+
1179
1217
var value = node .value .accept (this );
1180
1218
1181
1219
var readNode = (ReadConstantNode ) node .target .accept (this );
@@ -1464,6 +1502,9 @@ public RubyNode visitForwardingSuperNode(Nodes.ForwardingSuperNode node) {
1464
1502
1465
1503
@ Override
1466
1504
public RubyNode visitGlobalVariableAndWriteNode (Nodes .GlobalVariableAndWriteNode node ) {
1505
+ // `$a &&= value` is translated into `$a && $a = value`
1506
+ // don't check whether variable is defined so a warning will be emitted otherwise
1507
+
1467
1508
int startOffset = node .startOffset ;
1468
1509
int length = node .length ;
1469
1510
@@ -1477,6 +1518,9 @@ public RubyNode visitGlobalVariableAndWriteNode(Nodes.GlobalVariableAndWriteNode
1477
1518
1478
1519
@ Override
1479
1520
public RubyNode visitGlobalVariableOperatorWriteNode (Nodes .GlobalVariableOperatorWriteNode node ) {
1521
+ // e.g. `$a += value` is translated into $a = $a + value`
1522
+ // don't check whether variable is initialized so exception will be raised otherwise
1523
+
1480
1524
int startOffset = node .startOffset ;
1481
1525
int length = node .length ;
1482
1526
var readNode = new Nodes .GlobalVariableReadNode (node .name , startOffset , length );
@@ -1488,11 +1532,15 @@ public RubyNode visitGlobalVariableOperatorWriteNode(Nodes.GlobalVariableOperato
1488
1532
@ Override
1489
1533
public RubyNode visitGlobalVariableOrWriteNode (Nodes .GlobalVariableOrWriteNode node ) {
1490
1534
// `$a ||= value` is translated into `(defined?($a) && $a) || $a = value`
1535
+ // check whether variable is defined so no warnings will be emitted otherwise
1536
+
1491
1537
int startOffset = node .startOffset ;
1492
1538
int length = node .length ;
1539
+
1493
1540
var writeNode = new Nodes .GlobalVariableWriteNode (node .name , node .value , startOffset , length ).accept (this );
1494
1541
var readNode = new Nodes .GlobalVariableReadNode (node .name , startOffset , length ).accept (this );
1495
1542
var definedCheck = AndNodeGen .create (new DefinedNode (readNode ), readNode );
1543
+
1496
1544
final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (definedCheck , writeNode );
1497
1545
return assignPositionAndFlags (node , rubyNode );
1498
1546
}
@@ -1629,12 +1677,13 @@ public RubyNode visitInNode(Nodes.InNode node) {
1629
1677
1630
1678
@ Override
1631
1679
public RubyNode visitIndexAndWriteNode (Nodes .IndexAndWriteNode node ) {
1632
- assert node .receiver != null ;
1633
-
1634
1680
// `a[b] &&= c` is translated into `a[b] && a[b] = c`
1681
+
1635
1682
// receiver (a) and arguments (b) should be executed only once
1636
1683
// that's why they are cached in local variables
1637
1684
1685
+ assert node .receiver != null ;
1686
+
1638
1687
// receiver
1639
1688
final var receiverExpression = new YARPExecutedOnceExpression ("opelementassign" , node .receiver , this );
1640
1689
final var writeReceiveNode = receiverExpression .getWriteNode ();
@@ -1699,12 +1748,13 @@ public RubyNode visitIndexAndWriteNode(Nodes.IndexAndWriteNode node) {
1699
1748
1700
1749
@ Override
1701
1750
public RubyNode visitIndexOperatorWriteNode (Nodes .IndexOperatorWriteNode node ) {
1702
- assert node . receiver != null ;
1751
+ // e.g. `a[b] += value` is translated into `a[b] = a[b] + value`,
1703
1752
1704
- // `a[b] += c` is translated into `a[b] = a[b] + c`
1705
- // receiver (a) and arguments (b) should be executed only once
1753
+ // receiver (a) and arguments (b) should be executed only once -
1706
1754
// that's why they are cached in local variables
1707
1755
1756
+ assert node .receiver != null ;
1757
+
1708
1758
// receiver
1709
1759
final var receiverExpression = new YARPExecutedOnceExpression ("opelementassign" , node .receiver , this );
1710
1760
final var writeReceiveNode = receiverExpression .getWriteNode ();
@@ -1768,12 +1818,13 @@ public RubyNode visitIndexOperatorWriteNode(Nodes.IndexOperatorWriteNode node) {
1768
1818
1769
1819
@ Override
1770
1820
public RubyNode visitIndexOrWriteNode (Nodes .IndexOrWriteNode node ) {
1771
- assert node .receiver != null ;
1772
-
1773
1821
// `a[b] ||= c` is translated into `a[b] || a[b] = c`
1822
+
1774
1823
// receiver (a) and arguments (b) should be executed only once
1775
1824
// that's why they are cached in local variables
1776
1825
1826
+ assert node .receiver != null ;
1827
+
1777
1828
// receiver
1778
1829
final var receiverExpression = new YARPExecutedOnceExpression ("opelementassign" , node .receiver , this );
1779
1830
final var writeReceiveNode = receiverExpression .getWriteNode ();
@@ -1838,6 +1889,10 @@ public RubyNode visitIndexOrWriteNode(Nodes.IndexOrWriteNode node) {
1838
1889
1839
1890
@ Override
1840
1891
public RubyNode visitInstanceVariableAndWriteNode (Nodes .InstanceVariableAndWriteNode node ) {
1892
+ // `@a &&= value` is translated into `@a && @a = value`
1893
+ // don't check whether variable is initialized because even if an instance variable
1894
+ // is not set then it returns nil and does not have side effects (warnings or exceptions)
1895
+
1841
1896
int startOffset = node .startOffset ;
1842
1897
int length = node .length ;
1843
1898
@@ -1851,8 +1906,12 @@ public RubyNode visitInstanceVariableAndWriteNode(Nodes.InstanceVariableAndWrite
1851
1906
1852
1907
@ Override
1853
1908
public RubyNode visitInstanceVariableOperatorWriteNode (Nodes .InstanceVariableOperatorWriteNode node ) {
1909
+ // e.g. `@a += value` is translated into @a = @a + value`
1910
+ // don't check whether variable is defined so exception will be raised otherwise
1911
+
1854
1912
int startOffset = node .startOffset ;
1855
1913
int length = node .length ;
1914
+
1856
1915
var readNode = new Nodes .InstanceVariableReadNode (node .name , startOffset , length );
1857
1916
var desugared = new Nodes .InstanceVariableWriteNode (node .name ,
1858
1917
callNode (node , readNode , node .operator , node .value ), startOffset , length );
@@ -1865,10 +1924,13 @@ public RubyNode visitInstanceVariableOrWriteNode(Nodes.InstanceVariableOrWriteNo
1865
1924
1866
1925
// No need to check `defined?(@ivar)` before reading, as `@ivar` even if not set returns nil and does not have
1867
1926
// side effects (warnings or exceptions)
1927
+
1868
1928
int startOffset = node .startOffset ;
1869
1929
int length = node .length ;
1930
+
1870
1931
var writeNode = new Nodes .InstanceVariableWriteNode (node .name , node .value , startOffset , length ).accept (this );
1871
1932
var readNode = new Nodes .InstanceVariableReadNode (node .name , startOffset , length ).accept (this );
1933
+
1872
1934
final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (readNode , writeNode );
1873
1935
return assignPositionAndFlags (node , rubyNode );
1874
1936
}
@@ -2025,6 +2087,10 @@ public RubyNode visitLocalVariableReadNode(Nodes.LocalVariableReadNode node) {
2025
2087
2026
2088
@ Override
2027
2089
public RubyNode visitLocalVariableAndWriteNode (Nodes .LocalVariableAndWriteNode node ) {
2090
+ // `a &&= value` is translated into `a && a = value`
2091
+ // don't check whether variable is initialized because even if a local variable
2092
+ // is not set then it returns nil and does not have side effects (warnings or exceptions)
2093
+
2028
2094
int startOffset = node .startOffset ;
2029
2095
int length = node .length ;
2030
2096
@@ -2039,6 +2105,9 @@ public RubyNode visitLocalVariableAndWriteNode(Nodes.LocalVariableAndWriteNode n
2039
2105
2040
2106
@ Override
2041
2107
public RubyNode visitLocalVariableOperatorWriteNode (Nodes .LocalVariableOperatorWriteNode node ) {
2108
+ // e.g. `a += value` is translated into a = a + value`
2109
+ // don't check whether variable is initialized so exception will be raised otherwise
2110
+
2042
2111
int startOffset = node .startOffset ;
2043
2112
int length = node .length ;
2044
2113
var readNode = new Nodes .LocalVariableReadNode (node .name , node .depth , startOffset , length );
@@ -2055,9 +2124,11 @@ public RubyNode visitLocalVariableOrWriteNode(Nodes.LocalVariableOrWriteNode nod
2055
2124
// side effects (warnings or exceptions)
2056
2125
int startOffset = node .startOffset ;
2057
2126
int length = node .length ;
2127
+
2058
2128
var writeNode = new Nodes .LocalVariableWriteNode (node .name , node .depth , node .value , startOffset , length )
2059
2129
.accept (this );
2060
2130
var readNode = new Nodes .LocalVariableReadNode (node .name , node .depth , startOffset , length ).accept (this );
2131
+
2061
2132
final RubyNode rubyNode = OrLazyValueDefinedNodeGen .create (readNode , writeNode );
2062
2133
return assignPositionAndFlags (node , rubyNode );
2063
2134
}
0 commit comments