71
71
import static com .oracle .graal .python .nodes .SpecialMethodNames .__COMPLEX__ ;
72
72
import static com .oracle .graal .python .nodes .SpecialMethodNames .__INDEX__ ;
73
73
import static com .oracle .graal .python .nodes .SpecialMethodNames .__INT__ ;
74
+ import static com .oracle .graal .python .nodes .SpecialMethodNames .__REPR__ ;
74
75
import static com .oracle .graal .python .nodes .SpecialMethodNames .__SETITEM__ ;
75
76
import static com .oracle .graal .python .nodes .SpecialMethodNames .__TRUNC__ ;
76
77
import static com .oracle .graal .python .runtime .exception .PythonErrorType .NotImplementedError ;
@@ -1153,6 +1154,7 @@ public abstract static class IntNode extends PythonBuiltinNode {
1153
1154
@ Child private LookupAndCallUnaryNode callIntNode ;
1154
1155
@ Child private LookupAndCallUnaryNode callIndexNode ;
1155
1156
@ Child private LookupAndCallUnaryNode callTruncNode ;
1157
+ @ Child private LookupAndCallUnaryNode callReprNode ;
1156
1158
1157
1159
public abstract Object executeWith (VirtualFrame frame , Object cls , Object arg , Object base );
1158
1160
@@ -1171,11 +1173,24 @@ private static Object stringToIntInternal(String num, int base) {
1171
1173
}
1172
1174
}
1173
1175
1174
- private Object stringToInt (LazyPythonClass cls , String number , int base ) {
1176
+ private Object stringToInt (VirtualFrame frame , LazyPythonClass cls , String number , int base , Object origObj ) {
1175
1177
Object value = stringToIntInternal (number , base );
1176
1178
if (value == null ) {
1177
1179
invalidValueProfile .enter ();
1178
- throw raise (ValueError , ErrorMessages .INVALID_LITERAL_FOR_INT_WITH_BASE , base , number );
1180
+ if (callReprNode == null ) {
1181
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1182
+ callReprNode = insert (LookupAndCallUnaryNode .create (__REPR__ ));
1183
+ }
1184
+ Object str = callReprNode .executeObject (frame , origObj );
1185
+ if (PGuards .isString (str )) {
1186
+ throw raise (ValueError , ErrorMessages .INVALID_LITERAL_FOR_INT_WITH_BASE , base , str );
1187
+ } else {
1188
+ // During the formatting of "ValueError: invalid literal ..." exception,
1189
+ // CPython attempts to raise "TypeError: __repr__ returned non-string",
1190
+ // which gets later overwitten with the original "ValueError",
1191
+ // but without any message (since the message formatting failed)
1192
+ throw raise (ValueError );
1193
+ }
1179
1194
}
1180
1195
return createInt (cls , value );
1181
1196
}
@@ -1204,7 +1219,7 @@ private Object createInt(LazyPythonClass cls, Object value) {
1204
1219
}
1205
1220
1206
1221
private void checkBase (int base ) {
1207
- if (invalidBase .profile ((base < 2 || base > 32 ) && base != 0 )) {
1222
+ if (invalidBase .profile ((base < 2 || base > 36 ) && base != 0 )) {
1208
1223
throw raise (ValueError , ErrorMessages .BASE_OUT_OF_RANGE_FOR_INT );
1209
1224
}
1210
1225
}
@@ -1403,8 +1418,8 @@ long createLongBase10(@SuppressWarnings("unused") LazyPythonClass cls, String ar
1403
1418
}
1404
1419
1405
1420
@ Specialization (guards = "isNoValue(base)" )
1406
- Object createInt (LazyPythonClass cls , String arg , @ SuppressWarnings ("unused" ) PNone base ) {
1407
- return stringToInt (cls , arg , 10 );
1421
+ Object createInt (VirtualFrame frame , LazyPythonClass cls , String arg , @ SuppressWarnings ("unused" ) PNone base ) {
1422
+ return stringToInt (frame , cls , arg , 10 , arg );
1408
1423
}
1409
1424
1410
1425
@ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
@@ -1420,17 +1435,17 @@ long parseLong(@SuppressWarnings("unused") LazyPythonClass cls, String arg, int
1420
1435
}
1421
1436
1422
1437
@ Specialization
1423
- Object parsePIntError (LazyPythonClass cls , String number , int base ) {
1438
+ Object parsePIntError (VirtualFrame frame , LazyPythonClass cls , String number , int base ) {
1424
1439
checkBase (base );
1425
- return stringToInt (cls , number , base );
1440
+ return stringToInt (frame , cls , number , base , number );
1426
1441
}
1427
1442
1428
1443
@ Specialization (guards = "!isNoValue(base)" , limit = "getCallSiteInlineCacheMaxDepth()" )
1429
1444
Object createIntError (VirtualFrame frame , LazyPythonClass cls , String number , Object base ,
1430
1445
@ CachedLibrary ("base" ) PythonObjectLibrary lib ) {
1431
1446
int intBase = lib .asSizeWithState (base , PArguments .getThreadState (frame ));
1432
1447
checkBase (intBase );
1433
- return stringToInt (cls , number , intBase );
1448
+ return stringToInt (frame , cls , number , intBase , number );
1434
1449
}
1435
1450
1436
1451
// PIBytesLike
@@ -1448,7 +1463,7 @@ long parseLong(VirtualFrame frame, LazyPythonClass cls, PIBytesLike arg, int bas
1448
1463
@ Specialization
1449
1464
Object parseBytesError (VirtualFrame frame , LazyPythonClass cls , PIBytesLike arg , int base ) {
1450
1465
checkBase (base );
1451
- return stringToInt (cls , toString (frame , arg ), base );
1466
+ return stringToInt (frame , cls , toString (frame , arg ), base , arg );
1452
1467
}
1453
1468
1454
1469
@ Specialization (guards = "isNoValue(base)" )
@@ -1469,8 +1484,8 @@ long createLong(@SuppressWarnings("unused") LazyPythonClass cls, PString arg, @S
1469
1484
}
1470
1485
1471
1486
@ Specialization (guards = "isNoValue(base)" )
1472
- Object parsePInt (LazyPythonClass cls , PString arg , @ SuppressWarnings ("unused" ) PNone base ) {
1473
- return stringToInt (cls , arg .getValue (), 10 );
1487
+ Object parsePInt (VirtualFrame frame , LazyPythonClass cls , PString arg , @ SuppressWarnings ("unused" ) PNone base ) {
1488
+ return stringToInt (frame , cls , arg .getValue (), 10 , arg );
1474
1489
}
1475
1490
1476
1491
@ Specialization (guards = "isPrimitiveInt(cls)" , rewriteOn = NumberFormatException .class )
@@ -1486,9 +1501,9 @@ long parseLong(@SuppressWarnings("unused") LazyPythonClass cls, PString arg, int
1486
1501
}
1487
1502
1488
1503
@ Specialization
1489
- Object parsePInt (LazyPythonClass cls , PString arg , int base ) {
1504
+ Object parsePInt (VirtualFrame frame , LazyPythonClass cls , PString arg , int base ) {
1490
1505
checkBase (base );
1491
- return stringToInt (cls , arg .getValue (), base );
1506
+ return stringToInt (frame , cls , arg .getValue (), base , arg );
1492
1507
}
1493
1508
1494
1509
// other
@@ -1512,28 +1527,42 @@ Object createInt(LazyPythonClass cls, @SuppressWarnings("unused") PNone none, @S
1512
1527
}
1513
1528
1514
1529
@ SuppressWarnings ("unused" )
1515
- @ Specialization (guards = {"!isString(arg)" , "!isNoValue(base)" })
1530
+ @ Specialization (guards = {"!isString(arg)" , "!isBytes(arg)" , "! isNoValue(base)" })
1516
1531
Object fail (LazyPythonClass cls , Object arg , Object base ) {
1517
- throw raise (TypeError , ErrorMessages .INT_CANT_CONVERT_STRING_EITH_EXPL_BASE );
1532
+ throw raise (TypeError , ErrorMessages .INT_CANT_CONVERT_STRING_WITH_EXPL_BASE );
1518
1533
}
1519
1534
1520
1535
@ Specialization (guards = {"isNoValue(base)" , "!isNoValue(obj)" , "!isHandledType(obj)" })
1521
1536
Object createIntGeneric (VirtualFrame frame , LazyPythonClass cls , Object obj , @ SuppressWarnings ("unused" ) PNone base ) {
1537
+ // This method (together with callInt and callIndex) reflects the logic of PyNumber_Long
1538
+ // in CPython. We don't use PythonObjectLibrary here since the original CPython function
1539
+ // does not use any of the conversion functions (such as _PyLong_AsInt or
1540
+ // PyNumber_Index) either, but it reimplements the logic in a slightly different way
1541
+ // (e.g. trying __int__ before __index__ whereas _PyLong_AsInt does it the other way)
1542
+ // and also with specific exception messages which are expected by Python unittests.
1543
+ // This unfortunately means that this method relies on the internal logic of NO_VALUE
1544
+ // return values representing missing magic methods which should be ideally hidden
1545
+ // by PythonObjectLibrary.
1522
1546
Object result = callInt (frame , obj );
1523
1547
if (result == PNone .NO_VALUE ) {
1524
1548
result = callIndex (frame , obj );
1525
1549
if (result == PNone .NO_VALUE ) {
1526
- result = callTrunc (frame , obj );
1527
- if (result == PNone .NO_VALUE ) {
1550
+ Object truncResult = callTrunc (frame , obj );
1551
+ if (truncResult == PNone .NO_VALUE ) {
1528
1552
throw raise (TypeError , ErrorMessages .ARG_MUST_BE_STRING_OR_BYTELIKE_OR_NUMBER , "int()" , obj );
1529
- } else if (!isIntegerType (result )) {
1530
- throw raise (TypeError , ErrorMessages .RETURNED_NON_INT , "__trunc__" , result );
1531
1553
}
1532
- } else if (!isIntegerType (result )) {
1533
- throw raise (TypeError , ErrorMessages .RETURNED_NON_INT , "__index__" , result );
1554
+ if (isIntegerType (truncResult )) {
1555
+ result = truncResult ;
1556
+ } else {
1557
+ result = callIndex (frame , truncResult );
1558
+ if (result == PNone .NO_VALUE ) {
1559
+ result = callInt (frame , obj );
1560
+ if (result == PNone .NO_VALUE ) {
1561
+ throw raise (TypeError , ErrorMessages .RETURNED_NON_INTEGRAL , "__trunc__" , truncResult );
1562
+ }
1563
+ }
1564
+ }
1534
1565
}
1535
- } else if (!isIntegerType (result )) {
1536
- throw raise (TypeError , ErrorMessages .RETURNED_NON_INT , "__int__" , result );
1537
1566
}
1538
1567
return createInt (cls , result );
1539
1568
}
@@ -1551,19 +1580,31 @@ protected static FloatBuiltins.IntNode createFloatInt() {
1551
1580
}
1552
1581
1553
1582
private Object callInt (VirtualFrame frame , Object obj ) {
1583
+ // The case when the result is NO_VALUE (i.e. the object does not provide __int__)
1584
+ // is handled in createIntGeneric
1554
1585
if (callIntNode == null ) {
1555
1586
CompilerDirectives .transferToInterpreterAndInvalidate ();
1556
1587
callIntNode = insert (LookupAndCallUnaryNode .create (__INT__ ));
1557
1588
}
1558
- return callIntNode .executeObject (frame , obj );
1589
+ Object result = callIntNode .executeObject (frame , obj );
1590
+ if (result != PNone .NO_VALUE && !isIntegerType (result )) {
1591
+ throw raise (TypeError , ErrorMessages .RETURNED_NON_INT , "__int__" , result );
1592
+ }
1593
+ return result ;
1559
1594
}
1560
1595
1561
1596
private Object callIndex (VirtualFrame frame , Object obj ) {
1562
1597
if (callIndexNode == null ) {
1563
1598
CompilerDirectives .transferToInterpreterAndInvalidate ();
1564
1599
callIndexNode = insert (LookupAndCallUnaryNode .create (__INDEX__ ));
1565
1600
}
1566
- return callIndexNode .executeObject (frame , obj );
1601
+ Object result = callIndexNode .executeObject (frame , obj );
1602
+ // the case when the result is NO_VALUE (i.e. the object does not provide __index__)
1603
+ // is handled in createIntGeneric
1604
+ if (result != PNone .NO_VALUE && !isIntegerType (result )) {
1605
+ throw raise (TypeError , ErrorMessages .RETURNED_NON_INT , "__index__" , result );
1606
+ }
1607
+ return result ;
1567
1608
}
1568
1609
1569
1610
private Object callTrunc (VirtualFrame frame , Object obj ) {
0 commit comments