@@ -202,14 +202,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
202
202
val hasElse = ! elsep.isEmpty
203
203
val postIf = if (hasElse) new asm.Label else failure
204
204
205
- genCond(condp, success, failure)
205
+ genCond(condp, success, failure, targetIfNoJump = success)
206
+ markProgramPoint(success)
206
207
207
208
val thenKind = tpeTK(thenp)
208
209
val elseKind = if (! hasElse) UNIT else tpeTK(elsep)
209
210
def hasUnitBranch = (thenKind == UNIT || elseKind == UNIT )
210
211
val resKind = if (hasUnitBranch) UNIT else tpeTK(tree)
211
212
212
- markProgramPoint(success)
213
213
genLoad(thenp, resKind)
214
214
if (hasElse) { bc goTo postIf }
215
215
markProgramPoint(failure)
@@ -234,14 +234,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
234
234
else if (isArrayOp(code)) genArrayOp(tree, code, expectedType)
235
235
else if (isLogicalOp(code) || isComparisonOp(code)) {
236
236
val success, failure, after = new asm.Label
237
- genCond(tree, success, failure)
237
+ genCond(tree, success, failure, targetIfNoJump = success )
238
238
// success block
239
- markProgramPoint(success)
240
- bc boolconst true
241
- bc goTo after
239
+ markProgramPoint(success)
240
+ bc boolconst true
241
+ bc goTo after
242
242
// failure block
243
- markProgramPoint(failure)
244
- bc boolconst false
243
+ markProgramPoint(failure)
244
+ bc boolconst false
245
245
// after
246
246
markProgramPoint(after)
247
247
@@ -717,7 +717,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
717
717
// for callsites marked `f(): @inline/noinline`. For nullary calls, the attachment
718
718
// is on the Select node (not on the Apply node added by UnCurry).
719
719
def checkInlineAnnotated (t : Tree ): Unit = {
720
- if (t.hasAttachment[InlineAnnotatedAttachment ]) bc.jmethod.instructions.getLast match {
720
+ if (t.hasAttachment[InlineAnnotatedAttachment ]) lastInsn match {
721
721
case m : MethodInsnNode =>
722
722
if (app.hasAttachment[NoInlineCallsiteAttachment .type ]) noInlineAnnotatedCallsites += m
723
723
else inlineAnnotatedCallsites += m
@@ -888,10 +888,24 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
888
888
* emitted instruction was an ATHROW. As explained above, it is OK to emit a second ATHROW,
889
889
* the verifiers will be happy.
890
890
*/
891
- emit(asm.Opcodes .ATHROW )
891
+ if (lastInsn.getOpcode != asm.Opcodes .ATHROW )
892
+ emit(asm.Opcodes .ATHROW )
892
893
} else if (from.isNullType) {
893
- bc drop from
894
- emit(asm.Opcodes .ACONST_NULL )
894
+ /* After loading an expression of type `scala.runtime.Null$`, introduce POP; ACONST_NULL.
895
+ * This is required to pass the verifier: in Scala's type system, Null conforms to any
896
+ * reference type. In bytecode, the type Null is represented by scala.runtime.Null$, which
897
+ * is not a subtype of all reference types. Example:
898
+ *
899
+ * def nl: Null = null // in bytecode, nl has return type scala.runtime.Null$
900
+ * val a: String = nl // OK for Scala but not for the JVM, scala.runtime.Null$ does not conform to String
901
+ *
902
+ * In order to fix the above problem, the value returned by nl is dropped and ACONST_NULL is
903
+ * inserted instead - after all, an expression of type scala.runtime.Null$ can only be null.
904
+ */
905
+ if (lastInsn.getOpcode != asm.Opcodes .ACONST_NULL ) {
906
+ bc drop from
907
+ emit(asm.Opcodes .ACONST_NULL )
908
+ }
895
909
}
896
910
else (from, to) match {
897
911
case (BYTE , LONG ) | (SHORT , LONG ) | (CHAR , LONG ) | (INT , LONG ) => bc.emitT2T(INT , LONG )
@@ -1108,53 +1122,58 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1108
1122
}
1109
1123
1110
1124
/* Emit code to compare the two top-most stack values using the 'op' operator. */
1111
- private def genCJUMP (success : asm.Label , failure : asm.Label , op : TestOp , tk : BType ) {
1112
- if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1113
- bc.emitIF_ICMP(op, success)
1114
- } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1115
- bc.emitIF_ACMP(op, success)
1116
- } else {
1117
- (tk : @ unchecked) match {
1118
- case LONG => emit(asm.Opcodes .LCMP )
1119
- case FLOAT =>
1120
- if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .FCMPG )
1121
- else emit(asm.Opcodes .FCMPL )
1122
- case DOUBLE =>
1123
- if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .DCMPG )
1124
- else emit(asm.Opcodes .DCMPL )
1125
+ private def genCJUMP (success : asm.Label , failure : asm.Label , op : TestOp , tk : BType , targetIfNoJump : asm.Label ) {
1126
+ if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump)
1127
+ else {
1128
+ if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1129
+ bc.emitIF_ICMP(op, success)
1130
+ } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1131
+ bc.emitIF_ACMP(op, success)
1132
+ } else {
1133
+ (tk : @ unchecked) match {
1134
+ case LONG => emit(asm.Opcodes .LCMP )
1135
+ case FLOAT =>
1136
+ if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .FCMPG )
1137
+ else emit(asm.Opcodes .FCMPL )
1138
+ case DOUBLE =>
1139
+ if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .DCMPG )
1140
+ else emit(asm.Opcodes .DCMPL )
1141
+ }
1142
+ bc.emitIF(op, success)
1125
1143
}
1126
- bc.emitIF(op, success)
1144
+ if (targetIfNoJump != failure) bc goTo failure
1127
1145
}
1128
- bc goTo failure
1129
1146
}
1130
1147
1131
1148
/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
1132
- private def genCZJUMP (success : asm.Label , failure : asm.Label , op : TestOp , tk : BType ) {
1133
- if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1134
- bc.emitIF(op, success)
1135
- } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1136
- // @unchecked because references aren't compared with GT, GE, LT, LE.
1137
- (op : @ unchecked) match {
1138
- case TestOp .EQ => bc emitIFNULL success
1139
- case TestOp .NE => bc emitIFNONNULL success
1140
- }
1141
- } else {
1142
- (tk : @ unchecked) match {
1143
- case LONG =>
1144
- emit(asm.Opcodes .LCONST_0 )
1145
- emit(asm.Opcodes .LCMP )
1146
- case FLOAT =>
1147
- emit(asm.Opcodes .FCONST_0 )
1148
- if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .FCMPG )
1149
- else emit(asm.Opcodes .FCMPL )
1150
- case DOUBLE =>
1151
- emit(asm.Opcodes .DCONST_0 )
1152
- if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .DCMPG )
1153
- else emit(asm.Opcodes .DCMPL )
1149
+ private def genCZJUMP (success : asm.Label , failure : asm.Label , op : TestOp , tk : BType , targetIfNoJump : asm.Label ) {
1150
+ if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump)
1151
+ else {
1152
+ if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
1153
+ bc.emitIF(op, success)
1154
+ } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
1155
+ op match { // references are only compared with EQ and NE
1156
+ case TestOp .EQ => bc emitIFNULL success
1157
+ case TestOp .NE => bc emitIFNONNULL success
1158
+ }
1159
+ } else {
1160
+ (tk : @ unchecked) match {
1161
+ case LONG =>
1162
+ emit(asm.Opcodes .LCONST_0 )
1163
+ emit(asm.Opcodes .LCMP )
1164
+ case FLOAT =>
1165
+ emit(asm.Opcodes .FCONST_0 )
1166
+ if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .FCMPG )
1167
+ else emit(asm.Opcodes .FCMPL )
1168
+ case DOUBLE =>
1169
+ emit(asm.Opcodes .DCONST_0 )
1170
+ if (op == TestOp .LT || op == TestOp .LE ) emit(asm.Opcodes .DCMPG )
1171
+ else emit(asm.Opcodes .DCMPL )
1172
+ }
1173
+ bc.emitIF(op, success)
1154
1174
}
1155
- bc.emitIF(op, success)
1175
+ if (targetIfNoJump != failure) bc goTo failure
1156
1176
}
1157
- bc goTo failure
1158
1177
}
1159
1178
1160
1179
def testOpForPrimitive (primitiveCode : Int ) = (primitiveCode : @ switch) match {
@@ -1179,29 +1198,26 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1179
1198
* Generate code for conditional expressions.
1180
1199
* The jump targets success/failure of the test are `then-target` and `else-target` resp.
1181
1200
*/
1182
- private def genCond (tree : Tree , success : asm.Label , failure : asm.Label ) {
1201
+ private def genCond (tree : Tree , success : asm.Label , failure : asm.Label , targetIfNoJump : asm. Label ) {
1183
1202
1184
1203
def genComparisonOp (l : Tree , r : Tree , code : Int ) {
1185
- val op : TestOp = testOpForPrimitive(code)
1186
- // special-case reference (in)equality test for null (null eq x, x eq null)
1187
- var nonNullSide : Tree = null
1188
- if (scalaPrimitives.isReferenceEqualityOp(code) &&
1189
- { nonNullSide = ifOneIsNull(l, r); nonNullSide != null }
1190
- ) {
1204
+ val op = testOpForPrimitive(code)
1205
+ val nonNullSide = if (scalaPrimitives.isReferenceEqualityOp(code)) ifOneIsNull(l, r) else null
1206
+ if (nonNullSide != null ) {
1207
+ // special-case reference (in)equality test for null (null eq x, x eq null)
1191
1208
genLoad(nonNullSide, ObjectRef )
1192
- genCZJUMP(success, failure, op, ObjectRef )
1193
- }
1194
- else {
1209
+ genCZJUMP(success, failure, op, ObjectRef , targetIfNoJump)
1210
+ } else {
1195
1211
val tk = tpeTK(l).maxType(tpeTK(r))
1196
1212
genLoad(l, tk)
1197
1213
genLoad(r, tk)
1198
- genCJUMP(success, failure, op, tk)
1214
+ genCJUMP(success, failure, op, tk, targetIfNoJump )
1199
1215
}
1200
1216
}
1201
1217
1202
- def default () = {
1218
+ def loadAndTestBoolean () = {
1203
1219
genLoad(tree, BOOL )
1204
- genCZJUMP(success, failure, TestOp .NE , BOOL )
1220
+ genCZJUMP(success, failure, TestOp .NE , BOOL , targetIfNoJump )
1205
1221
}
1206
1222
1207
1223
lineNumber(tree)
@@ -1212,37 +1228,35 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1212
1228
1213
1229
// lhs and rhs of test
1214
1230
lazy val Select (lhs, _) = fun
1215
- val rhs = if (args.isEmpty) EmptyTree else args.head; // args.isEmpty only for ZNOT
1231
+ val rhs = if (args.isEmpty) EmptyTree else args.head // args.isEmpty only for ZNOT
1216
1232
1217
- def genZandOrZor (and : Boolean ) { // TODO WRONG
1233
+ def genZandOrZor (and : Boolean ) {
1218
1234
// reaching "keepGoing" indicates the rhs should be evaluated too (ie not short-circuited).
1219
1235
val keepGoing = new asm.Label
1220
1236
1221
- if (and) genCond(lhs, keepGoing, failure)
1222
- else genCond(lhs, success, keepGoing)
1237
+ if (and) genCond(lhs, keepGoing, failure, targetIfNoJump = keepGoing )
1238
+ else genCond(lhs, success, keepGoing, targetIfNoJump = keepGoing )
1223
1239
1224
1240
markProgramPoint(keepGoing)
1225
- genCond(rhs, success, failure)
1241
+ genCond(rhs, success, failure, targetIfNoJump )
1226
1242
}
1227
1243
1228
1244
getPrimitive(fun.symbol) match {
1229
- case ZNOT => genCond(lhs, failure, success)
1245
+ case ZNOT => genCond(lhs, failure, success, targetIfNoJump )
1230
1246
case ZAND => genZandOrZor(and = true )
1231
1247
case ZOR => genZandOrZor(and = false )
1232
1248
case code =>
1233
- // TODO !!!!!!!!!! isReferenceType, in the sense of TypeKind? (ie non-array, non-boxed, non-nothing, may be null)
1234
1249
if (scalaPrimitives.isUniversalEqualityOp(code) && tpeTK(lhs).isClass) {
1235
- // `lhs` has reference type
1236
- if (code == EQ ) genEqEqPrimitive(lhs, rhs, success, failure, tree.pos)
1237
- else genEqEqPrimitive(lhs, rhs, failure, success, tree.pos)
1238
- }
1239
- else if (scalaPrimitives.isComparisonOp(code))
1250
+ // rewrite `==` to null tests and `equals`. not needed for arrays (`equals` is reference equality).
1251
+ if (code == EQ ) genEqEqPrimitive(lhs, rhs, success, failure, targetIfNoJump, tree.pos)
1252
+ else genEqEqPrimitive(lhs, rhs, failure, success, targetIfNoJump, tree.pos)
1253
+ } else if (scalaPrimitives.isComparisonOp(code)) {
1240
1254
genComparisonOp(lhs, rhs, code)
1241
- else
1242
- default
1255
+ } else
1256
+ loadAndTestBoolean()
1243
1257
}
1244
1258
1245
- case _ => default
1259
+ case _ => loadAndTestBoolean()
1246
1260
}
1247
1261
1248
1262
} // end of genCond()
@@ -1254,7 +1268,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1254
1268
* @param l left-hand-side of the '=='
1255
1269
* @param r right-hand-side of the '=='
1256
1270
*/
1257
- def genEqEqPrimitive (l : Tree , r : Tree , success : asm.Label , failure : asm.Label , pos : Position ) {
1271
+ def genEqEqPrimitive (l : Tree , r : Tree , success : asm.Label , failure : asm.Label , targetIfNoJump : asm. Label , pos : Position ) {
1258
1272
1259
1273
/* True if the equality comparison is between values that require the use of the rich equality
1260
1274
* comparator (scala.runtime.Comparator.equals). This is the case when either side of the
@@ -1264,7 +1278,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1264
1278
*/
1265
1279
val mustUseAnyComparator : Boolean = {
1266
1280
val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe)
1267
-
1268
1281
! areSameFinals && platform.isMaybeBoxed(l.tpe.typeSymbol) && platform.isMaybeBoxed(r.tpe.typeSymbol)
1269
1282
}
1270
1283
@@ -1279,23 +1292,22 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1279
1292
genLoad(l, ObjectRef )
1280
1293
genLoad(r, ObjectRef )
1281
1294
genCallMethod(equalsMethod, InvokeStyle .Static , pos)
1282
- genCZJUMP(success, failure, TestOp .NE , BOOL )
1283
- }
1284
- else {
1295
+ genCZJUMP(success, failure, TestOp .NE , BOOL , targetIfNoJump)
1296
+ } else {
1285
1297
if (isNull(l)) {
1286
1298
// null == expr -> expr eq null
1287
1299
genLoad(r, ObjectRef )
1288
- genCZJUMP(success, failure, TestOp .EQ , ObjectRef )
1300
+ genCZJUMP(success, failure, TestOp .EQ , ObjectRef , targetIfNoJump )
1289
1301
} else if (isNull(r)) {
1290
1302
// expr == null -> expr eq null
1291
1303
genLoad(l, ObjectRef )
1292
- genCZJUMP(success, failure, TestOp .EQ , ObjectRef )
1304
+ genCZJUMP(success, failure, TestOp .EQ , ObjectRef , targetIfNoJump )
1293
1305
} else if (isNonNullExpr(l)) {
1294
1306
// SI-7852 Avoid null check if L is statically non-null.
1295
1307
genLoad(l, ObjectRef )
1296
1308
genLoad(r, ObjectRef )
1297
1309
genCallMethod(Object_equals , InvokeStyle .Virtual , pos)
1298
- genCZJUMP(success, failure, TestOp .NE , BOOL )
1310
+ genCZJUMP(success, failure, TestOp .NE , BOOL , targetIfNoJump )
1299
1311
} else {
1300
1312
// l == r -> if (l eq null) r eq null else l.equals(r)
1301
1313
val eqEqTempLocal = locals.makeLocal(ObjectRef , nme.EQEQ_LOCAL_VAR .toString)
@@ -1306,17 +1318,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
1306
1318
genLoad(r, ObjectRef )
1307
1319
locals.store(eqEqTempLocal)
1308
1320
bc dup ObjectRef
1309
- genCZJUMP(lNull, lNonNull, TestOp .EQ , ObjectRef )
1321
+ genCZJUMP(lNull, lNonNull, TestOp .EQ , ObjectRef , targetIfNoJump = lNull )
1310
1322
1311
1323
markProgramPoint(lNull)
1312
1324
bc drop ObjectRef
1313
1325
locals.load(eqEqTempLocal)
1314
- genCZJUMP(success, failure, TestOp .EQ , ObjectRef )
1326
+ genCZJUMP(success, failure, TestOp .EQ , ObjectRef , targetIfNoJump = lNonNull )
1315
1327
1316
1328
markProgramPoint(lNonNull)
1317
1329
locals.load(eqEqTempLocal)
1318
1330
genCallMethod(Object_equals , InvokeStyle .Virtual , pos)
1319
- genCZJUMP(success, failure, TestOp .NE , BOOL )
1331
+ genCZJUMP(success, failure, TestOp .NE , BOOL , targetIfNoJump )
1320
1332
}
1321
1333
}
1322
1334
}
0 commit comments