@@ -1148,6 +1148,7 @@ public abstract static class IntNode extends PythonTernaryBuiltinNode {
1148
1148
private final BranchProfile bigIntegerProfile = BranchProfile .create ();
1149
1149
private final BranchProfile primitiveIntProfile = BranchProfile .create ();
1150
1150
private final BranchProfile fullIntProfile = BranchProfile .create ();
1151
+ private final BranchProfile notSimpleDecimalLiteralProfile = BranchProfile .create ();
1151
1152
1152
1153
@ Child private BytesNodes .ToBytesNode toByteArrayNode ;
1153
1154
@ Child private LookupAndCallUnaryNode callIntNode ;
@@ -1157,9 +1158,8 @@ public abstract static class IntNode extends PythonTernaryBuiltinNode {
1157
1158
1158
1159
@ TruffleBoundary
1159
1160
private static Object stringToIntInternal (String num , int base ) {
1160
- String s = num .replace ("_" , "" );
1161
1161
try {
1162
- BigInteger bi = asciiToBigInteger (s , base , false );
1162
+ BigInteger bi = asciiToBigInteger (num , base );
1163
1163
if (bi .compareTo (BigInteger .valueOf (Integer .MAX_VALUE )) > 0 || bi .compareTo (BigInteger .valueOf (Integer .MIN_VALUE )) < 0 ) {
1164
1164
return bi ;
1165
1165
} else {
@@ -1171,6 +1171,13 @@ private static Object stringToIntInternal(String num, int base) {
1171
1171
}
1172
1172
1173
1173
private Object stringToInt (VirtualFrame frame , Object cls , String number , int base , Object origObj ) {
1174
+ if (base == 0 || base == 10 ) {
1175
+ Object value = parseSimpleDecimalLiteral (number );
1176
+ if (value != null ) {
1177
+ return createInt (cls , value );
1178
+ }
1179
+ }
1180
+ notSimpleDecimalLiteralProfile .enter ();
1174
1181
Object value = stringToIntInternal (number , base );
1175
1182
if (value == null ) {
1176
1183
invalidValueProfile .enter ();
@@ -1221,8 +1228,8 @@ private void checkBase(int base) {
1221
1228
}
1222
1229
}
1223
1230
1224
- // Copied directly from Jython
1225
- private static BigInteger asciiToBigInteger (String str , int possibleBase , boolean isLong ) throws NumberFormatException {
1231
+ // Adapted from Jython
1232
+ private static BigInteger asciiToBigInteger (String str , int possibleBase ) throws NumberFormatException {
1226
1233
CompilerAsserts .neverPartOfCompilation ();
1227
1234
int base = possibleBase ;
1228
1235
int b = 0 ;
@@ -1236,44 +1243,49 @@ private static BigInteger asciiToBigInteger(String str, int possibleBase, boolea
1236
1243
e --;
1237
1244
}
1238
1245
1246
+ boolean acceptUnderscore = false ;
1247
+ boolean raiseIfNotZero = false ;
1239
1248
char sign = 0 ;
1240
1249
if (b < e ) {
1241
1250
sign = str .charAt (b );
1242
1251
if (sign == '-' || sign == '+' ) {
1243
1252
b ++;
1244
- while (b < e && Character .isWhitespace (str .charAt (b ))) {
1245
- b ++;
1246
- }
1247
1253
}
1248
1254
1249
1255
if (base == 16 ) {
1250
1256
if (str .charAt (b ) == '0' ) {
1251
1257
if (b < e - 1 && Character .toUpperCase (str .charAt (b + 1 )) == 'X' ) {
1252
1258
b += 2 ;
1259
+ acceptUnderscore = true ;
1253
1260
}
1254
1261
}
1255
1262
} else if (base == 0 ) {
1256
1263
if (str .charAt (b ) == '0' ) {
1257
1264
if (b < e - 1 && Character .toUpperCase (str .charAt (b + 1 )) == 'X' ) {
1258
1265
base = 16 ;
1259
1266
b += 2 ;
1267
+ acceptUnderscore = true ;
1260
1268
} else if (b < e - 1 && Character .toUpperCase (str .charAt (b + 1 )) == 'O' ) {
1261
1269
base = 8 ;
1262
1270
b += 2 ;
1271
+ acceptUnderscore = true ;
1263
1272
} else if (b < e - 1 && Character .toUpperCase (str .charAt (b + 1 )) == 'B' ) {
1264
1273
base = 2 ;
1265
1274
b += 2 ;
1275
+ acceptUnderscore = true ;
1266
1276
} else {
1267
- base = 8 ;
1277
+ raiseIfNotZero = true ;
1268
1278
}
1269
1279
}
1270
1280
} else if (base == 8 ) {
1271
1281
if (b < e - 1 && Character .toUpperCase (str .charAt (b + 1 )) == 'O' ) {
1272
1282
b += 2 ;
1283
+ acceptUnderscore = true ;
1273
1284
}
1274
1285
} else if (base == 2 ) {
1275
1286
if (b < e - 1 && Character .toUpperCase (str .charAt (b + 1 )) == 'B' ) {
1276
1287
b += 2 ;
1288
+ acceptUnderscore = true ;
1277
1289
}
1278
1290
}
1279
1291
}
@@ -1282,80 +1294,76 @@ private static BigInteger asciiToBigInteger(String str, int possibleBase, boolea
1282
1294
base = 10 ;
1283
1295
}
1284
1296
1285
- // if the base >= 22, then an 'l' or 'L' is a digit!
1286
- if (isLong && base < 22 && e > b && (str .charAt (e - 1 ) == 'L' || str .charAt (e - 1 ) == 'l' )) {
1287
- e --;
1297
+ int i = b ;
1298
+ while (i < e ) {
1299
+ if (str .charAt (i ) == '_' ) {
1300
+ if (!acceptUnderscore || i == e - 1 ) {
1301
+ throw new NumberFormatException ("Illegal underscore in int literal" );
1302
+ } else {
1303
+ acceptUnderscore = false ;
1304
+ }
1305
+ } else {
1306
+ acceptUnderscore = true ;
1307
+ }
1308
+ ++i ;
1288
1309
}
1289
1310
1290
1311
String s = str ;
1291
1312
if (b > 0 || e < str .length ()) {
1292
1313
s = str .substring (b , e );
1293
1314
}
1315
+ s = s .replace ("_" , "" );
1294
1316
1295
1317
BigInteger bi ;
1296
1318
if (sign == '-' ) {
1297
1319
bi = new BigInteger ("-" + s , base );
1298
1320
} else {
1299
1321
bi = new BigInteger (s , base );
1300
1322
}
1323
+
1324
+ if (raiseIfNotZero && !bi .equals (BigInteger .ZERO )) {
1325
+ throw new NumberFormatException ("Obsolete octal int literal" );
1326
+ }
1301
1327
return bi ;
1302
1328
}
1303
1329
1304
- @ TruffleBoundary
1305
- private static int parseInt (String arg , int base ) {
1306
- if (arg .isEmpty () || base == 0 ) {
1307
- throw new NumberFormatException ();
1330
+ /**
1331
+ * Fast path parser of integer literals. Accepts only a subset of allowed literals - no
1332
+ * underscores, no leading zeros, no plus sign, no spaces, only ascii digits and the result
1333
+ * must be small enough to fit into long.
1334
+ *
1335
+ * @param arg the string to parse
1336
+ * @return parsed integer, long or null if the literal is not simple enough
1337
+ */
1338
+ private static Object parseSimpleDecimalLiteral (String arg ) {
1339
+ if (arg .isEmpty ()) {
1340
+ return null ;
1308
1341
}
1309
- boolean negative = arg .charAt (0 ) == '-' ;
1310
- int start = negative ? 1 : 0 ;
1311
- if (arg .length () <= start || arg .charAt (start ) == '_' ) {
1312
- throw new NumberFormatException ();
1342
+ int start = arg .charAt (0 ) == '-' ? 1 : 0 ;
1343
+ if (arg .length () <= start || arg .length () > 18 + start ) {
1344
+ return null ;
1313
1345
}
1314
- long value = 0 ;
1315
- for (int i = start ; i < arg .length (); i ++) {
1316
- char c = arg .charAt (i );
1317
- if (c == '_' ) {
1318
- continue ;
1319
- }
1320
- if (c < '0' || c > '9' ) {
1321
- throw new NumberFormatException ();
1322
- }
1323
- value = value * base + (c - '0' );
1324
- if (value > Integer .MAX_VALUE ) {
1325
- throw new NumberFormatException ();
1346
+ if (arg .charAt (start ) == '0' ) {
1347
+ if (arg .length () > start + 1 ) {
1348
+ return null ;
1326
1349
}
1327
- }
1328
- return (int ) (negative ? -value : value );
1329
- }
1330
-
1331
- private static final long MAX_VALUE = (Long .MAX_VALUE - 10 ) / 10 ;
1332
-
1333
- @ TruffleBoundary
1334
- private static long parseLong (String arg , int base ) {
1335
- if (arg .isEmpty () || base == 0 ) {
1336
- throw new NumberFormatException ();
1337
- }
1338
- boolean negative = arg .charAt (0 ) == '-' ;
1339
- int start = negative ? 1 : 0 ;
1340
- if (arg .length () <= start || arg .charAt (start ) == '_' ) {
1341
- throw new NumberFormatException ();
1350
+ return 0 ;
1342
1351
}
1343
1352
long value = 0 ;
1344
1353
for (int i = start ; i < arg .length (); i ++) {
1345
1354
char c = arg .charAt (i );
1346
- if (c == '_' ) {
1347
- continue ;
1348
- }
1349
1355
if (c < '0' || c > '9' ) {
1350
- throw new NumberFormatException ();
1351
- }
1352
- if (value >= MAX_VALUE ) {
1353
- // overflow, this will not allow Long.MIN_VALUE to be parsed
1354
- throw new NumberFormatException ();
1356
+ return null ;
1355
1357
}
1356
- value = value * base + (c - '0' );
1358
+ value = value * 10 + (c - '0' );
1357
1359
}
1358
- return negative ? -value : value ;
1360
+ if (start != 0 ) {
1361
+ value = -value ;
1362
+ }
1363
+ if (value >= Integer .MIN_VALUE && value <= Integer .MAX_VALUE ) {
1364
+ return (int ) value ;
1365
+ }
1366
+ return value ;
1359
1367
}
1360
1368
1361
1369
@ Child private IsBuiltinClassProfile isPrimitiveProfile = IsBuiltinClassProfile .create ();
@@ -1404,33 +1412,11 @@ Object createInt(Object cls, double arg, @SuppressWarnings("unused") PNone base,
1404
1412
1405
1413
// String
1406
1414
1407
- @ Specialization (guards = {"isNoValue(base)" , "isPrimitiveInt(cls)" }, rewriteOn = NumberFormatException .class )
1408
- int createIntBase10 (@ SuppressWarnings ("unused" ) Object cls , String arg , @ SuppressWarnings ("unused" ) PNone base ) throws NumberFormatException {
1409
- return parseInt (arg , 10 );
1410
- }
1411
-
1412
- @ Specialization (guards = {"isNoValue(base)" , "isPrimitiveInt(cls)" }, rewriteOn = NumberFormatException .class )
1413
- long createLongBase10 (@ SuppressWarnings ("unused" ) Object cls , String arg , @ SuppressWarnings ("unused" ) PNone base ) throws NumberFormatException {
1414
- return parseLong (arg , 10 );
1415
- }
1416
-
1417
1415
@ Specialization (guards = "isNoValue(base)" )
1418
1416
Object createInt (VirtualFrame frame , Object cls , String arg , @ SuppressWarnings ("unused" ) PNone base ) {
1419
1417
return stringToInt (frame , cls , arg , 10 , arg );
1420
1418
}
1421
1419
1422
- @ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
1423
- int parseInt (@ SuppressWarnings ("unused" ) Object cls , String arg , int base ) throws NumberFormatException {
1424
- checkBase (base );
1425
- return parseInt (arg , base );
1426
- }
1427
-
1428
- @ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
1429
- long parseLong (@ SuppressWarnings ("unused" ) Object cls , String arg , int base ) throws NumberFormatException {
1430
- checkBase (base );
1431
- return parseLong (arg , base );
1432
- }
1433
-
1434
1420
@ Specialization
1435
1421
Object parsePIntError (VirtualFrame frame , Object cls , String number , int base ) {
1436
1422
checkBase (base );
@@ -1447,16 +1433,6 @@ Object createIntError(VirtualFrame frame, Object cls, String number, Object base
1447
1433
1448
1434
// PIBytesLike
1449
1435
1450
- @ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
1451
- int parseInt (VirtualFrame frame , Object cls , PIBytesLike arg , int base ) throws NumberFormatException {
1452
- return parseInt (cls , toString (frame , arg ), base );
1453
- }
1454
-
1455
- @ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
1456
- long parseLong (VirtualFrame frame , Object cls , PIBytesLike arg , int base ) throws NumberFormatException {
1457
- return parseLong (cls , toString (frame , arg ), base );
1458
- }
1459
-
1460
1436
@ Specialization
1461
1437
Object parseBytesError (VirtualFrame frame , Object cls , PIBytesLike arg , int base ) {
1462
1438
checkBase (base );
@@ -1470,33 +1446,11 @@ Object parseBytesError(VirtualFrame frame, Object cls, PIBytesLike arg, @Suppres
1470
1446
1471
1447
// PString
1472
1448
1473
- @ Specialization (guards = {"isNoValue(base)" , "isPrimitiveInt(cls)" }, rewriteOn = NumberFormatException .class )
1474
- int createInt (@ SuppressWarnings ("unused" ) Object cls , PString arg , @ SuppressWarnings ("unused" ) PNone base ) throws NumberFormatException {
1475
- return parseInt (arg .getValue (), 10 );
1476
- }
1477
-
1478
- @ Specialization (guards = {"isNoValue(base)" , "isPrimitiveInt(cls)" }, rewriteOn = NumberFormatException .class )
1479
- long createLong (@ SuppressWarnings ("unused" ) Object cls , PString arg , @ SuppressWarnings ("unused" ) PNone base ) throws NumberFormatException {
1480
- return parseLong (arg .getValue (), 10 );
1481
- }
1482
-
1483
1449
@ Specialization (guards = "isNoValue(base)" )
1484
1450
Object parsePInt (VirtualFrame frame , Object cls , PString arg , @ SuppressWarnings ("unused" ) PNone base ) {
1485
1451
return stringToInt (frame , cls , arg .getValue (), 10 , arg );
1486
1452
}
1487
1453
1488
- @ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
1489
- int parseInt (@ SuppressWarnings ("unused" ) Object cls , PString arg , int base ) throws NumberFormatException {
1490
- checkBase (base );
1491
- return parseInt (arg .getValue (), base );
1492
- }
1493
-
1494
- @ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
1495
- long parseLong (@ SuppressWarnings ("unused" ) Object cls , PString arg , int base ) throws NumberFormatException {
1496
- checkBase (base );
1497
- return parseLong (arg .getValue (), base );
1498
- }
1499
-
1500
1454
@ Specialization
1501
1455
Object parsePInt (VirtualFrame frame , Object cls , PString arg , int base ) {
1502
1456
checkBase (base );
0 commit comments