74
74
import static com .oracle .graal .python .nodes .SpecialMethodNames .DECODE ;
75
75
import static com .oracle .graal .python .nodes .SpecialMethodNames .__COMPLEX__ ;
76
76
import static com .oracle .graal .python .nodes .SpecialMethodNames .__EQ__ ;
77
+ import static com .oracle .graal .python .nodes .SpecialMethodNames .__FLOAT__ ;
77
78
import static com .oracle .graal .python .nodes .SpecialMethodNames .__HASH__ ;
78
79
import static com .oracle .graal .python .nodes .SpecialMethodNames .__INDEX__ ;
79
80
import static com .oracle .graal .python .nodes .SpecialMethodNames .__INIT__ ;
@@ -914,8 +915,10 @@ public Object reversed(VirtualFrame frame, Object cls, Object sequence,
914
915
@ Builtin (name = FLOAT , minNumOfPositionalArgs = 1 , maxNumOfPositionalArgs = 2 , constructsClass = PythonBuiltinClassType .PFloat )
915
916
@ GenerateNodeFactory
916
917
@ ReportPolymorphism
917
- public abstract static class FloatNode extends PythonBuiltinNode {
918
+ public abstract static class FloatNode extends PythonBinaryBuiltinNode {
918
919
@ Child private BytesNodes .ToBytesNode toByteArrayNode ;
920
+ @ Child private LookupAndCallUnaryNode callFloatNode ;
921
+ @ Child private LookupAndCallUnaryNode callReprNode ;
919
922
920
923
@ Child private IsBuiltinClassProfile isPrimitiveProfile = IsBuiltinClassProfile .create ();
921
924
private ConditionProfile isNanProfile ;
@@ -971,43 +974,56 @@ Object floatFromDouble(Object cls, double arg) {
971
974
}
972
975
973
976
@ Specialization (guards = "!isNativeClass(cls)" )
974
- Object floatFromString (Object cls , String arg ) {
975
- double value = convertStringToDouble (arg );
976
- if (isPrimitiveFloat (cls )) {
977
- return value ;
978
- }
979
- return factoryCreateFloat (cls , value );
980
- }
981
-
982
- @ Specialization (guards = "!isNativeClass(cls)" )
983
- Object floatFromBytes (VirtualFrame frame , Object cls , PIBytesLike arg ) {
984
- double value = convertBytesToDouble (frame , arg );
977
+ Object floatFromString (VirtualFrame frame , Object cls , String arg ) {
978
+ double value = convertStringToDouble (frame , arg , arg );
985
979
if (isPrimitiveFloat (cls )) {
986
980
return value ;
987
981
}
988
982
return factoryCreateFloat (cls , value );
989
983
}
990
984
991
985
private double convertBytesToDouble (VirtualFrame frame , PIBytesLike arg ) {
992
- return convertStringToDouble (createString (getByteArray (frame , arg )));
986
+ return convertStringToDouble (frame , createString (getByteArray (frame , arg )), arg );
993
987
}
994
988
995
989
@ TruffleBoundary
996
990
private static String createString (byte [] bytes ) {
997
991
return new String (bytes );
998
992
}
999
993
1000
- // Taken from Jython PyString's atof() method
1001
- // The last statement throw Py.ValueError is modified
994
+ private double convertStringToDouble (VirtualFrame frame , String str , Object origObj ) {
995
+ try {
996
+ return convertStringToDoubleOrThrow (str );
997
+ } catch (NumberFormatException exc ) {
998
+ if (callReprNode == null ) {
999
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1000
+ callReprNode = insert (LookupAndCallUnaryNode .create (__REPR__ ));
1001
+ }
1002
+ Object strStr = callReprNode .executeObject (frame , origObj );
1003
+ if (PGuards .isString (strStr )) {
1004
+ throw raise (ValueError , ErrorMessages .COULD_NOT_CONVERT_STRING_TO_FLOAT , strStr );
1005
+ } else {
1006
+ // During the formatting of "ValueError: invalid literal ..." exception,
1007
+ // CPython attempts to raise "TypeError: __repr__ returned non-string",
1008
+ // which gets later overwitten with the original "ValueError",
1009
+ // but without any message (since the message formatting failed)
1010
+ throw raise (ValueError );
1011
+ }
1012
+ }
1013
+ }
1014
+
1015
+ // Adapted from Jython PyString's atof() method
1002
1016
@ TruffleBoundary
1003
- private double convertStringToDouble (String str ) {
1017
+ private static double convertStringToDoubleOrThrow (String str ) throws NumberFormatException {
1004
1018
StringBuilder s = null ;
1005
1019
int n = str .length ();
1006
1020
1021
+ boolean containsUnderscores = false ;
1022
+ boolean containsN = false ;
1007
1023
for (int i = 0 ; i < n ; i ++) {
1008
1024
char ch = str .charAt (i );
1009
- if (ch == '\u0000' ) {
1010
- throw raise ( ValueError , ErrorMessages . EMPTY_STR_FOR_COMPLEX );
1025
+ if (ch == '\u0000' || ch == 'x' || ch == 'X' ) {
1026
+ throw new NumberFormatException ( );
1011
1027
}
1012
1028
if (Character .isDigit (ch )) {
1013
1029
if (s == null ) {
@@ -1016,26 +1032,56 @@ private double convertStringToDouble(String str) {
1016
1032
int val = Character .digit (ch , 10 );
1017
1033
s .setCharAt (i , Character .forDigit (val , 10 ));
1018
1034
}
1035
+ if (Character .isWhitespace (ch )) {
1036
+ if (s == null ) {
1037
+ s = new StringBuilder (str );
1038
+ }
1039
+ s .setCharAt (i , ' ' );
1040
+ }
1041
+ containsUnderscores |= ch == '_' ;
1042
+ containsN |= ch == 'n' || ch == 'N' ;
1019
1043
}
1020
- String sval = str . trim ();
1021
- if (s != null ) {
1022
- sval = s . toString ( );
1044
+ String sval = s != null ? s . toString (). trim () : str ;
1045
+ if (containsUnderscores ) {
1046
+ sval = checkAndRemoveUnderscores ( sval );
1023
1047
}
1024
- try {
1025
- // Double.valueOf allows format specifier ("d" or "f") at the end
1048
+ if (containsN ) {
1026
1049
String lowSval = sval .toLowerCase (Locale .ENGLISH );
1027
- if (lowSval .equals ("nan" ) || lowSval .equals ("+nan" ) || lowSval . equals ( "-nan" ) ) {
1050
+ if (lowSval .equals ("nan" ) || lowSval .equals ("+nan" )) {
1028
1051
return Double .NaN ;
1052
+ } else if (lowSval .equals ("-nan" )) {
1053
+ return Math .copySign (Double .NaN , -1 );
1029
1054
} else if (lowSval .equals ("inf" ) || lowSval .equals ("+inf" ) || lowSval .equals ("infinity" ) || lowSval .equals ("+infinity" )) {
1030
1055
return Double .POSITIVE_INFINITY ;
1031
1056
} else if (lowSval .equals ("-inf" ) || lowSval .equals ("-infinity" )) {
1032
1057
return Double .NEGATIVE_INFINITY ;
1033
1058
}
1034
- return Double .valueOf (sval ).doubleValue ();
1035
- } catch (NumberFormatException exc ) {
1036
- // throw Py.ValueError("invalid literal for __float__: " + str);
1037
- throw raise (ValueError , ErrorMessages .COULD_NOT_CONVERT_STRING_TO_FLOAT , str );
1038
1059
}
1060
+ return Double .parseDouble (sval );
1061
+ }
1062
+
1063
+ private static String checkAndRemoveUnderscores (String src ) {
1064
+ StringBuilder sb = new StringBuilder ();
1065
+ char prev = 0 ;
1066
+ int len = src .length ();
1067
+ for (int i = 0 ; i < len ; i ++) {
1068
+ char ch = src .charAt (i );
1069
+ if (ch == '_' ) {
1070
+ if (!(prev >= '0' && prev <= '9' )) {
1071
+ throw new NumberFormatException ();
1072
+ }
1073
+ } else {
1074
+ if (prev == '_' && !(ch >= '0' && ch <= '9' )) {
1075
+ throw new NumberFormatException ();
1076
+ }
1077
+ sb .append (ch );
1078
+ }
1079
+ prev = ch ;
1080
+ }
1081
+ if (prev == '_' ) {
1082
+ throw new NumberFormatException ();
1083
+ }
1084
+ return sb .toString ();
1039
1085
}
1040
1086
1041
1087
@ Specialization (guards = "!isNativeClass(cls)" )
@@ -1046,29 +1092,62 @@ Object floatFromNone(Object cls, @SuppressWarnings("unused") PNone arg) {
1046
1092
return factory ().createFloat (cls , 0.0 );
1047
1093
}
1048
1094
1049
- @ Specialization (guards = "isPrimitiveFloat(cls)" )
1095
+ static boolean isHandledType (Object o ) {
1096
+ return PGuards .canBeInteger (o ) || PGuards .isDouble (o ) || o instanceof String || PGuards .isPNone (o );
1097
+ }
1098
+
1099
+ @ Specialization (guards = {"isPrimitiveFloat(cls)" , "!isHandledType(obj)" })
1050
1100
double doubleFromObject (VirtualFrame frame , @ SuppressWarnings ("unused" ) Object cls , Object obj ,
1051
1101
@ CachedLibrary (limit = "1" ) PythonObjectLibrary lib ) {
1052
- if (obj instanceof String ) {
1053
- return convertStringToDouble ((String ) obj );
1054
- } else if (obj instanceof PString ) {
1055
- return convertStringToDouble (((PString ) obj ).getValue ());
1056
- } else if (obj instanceof PNone ) {
1057
- return 0.0 ;
1102
+ // Follows logic from PyNumber_Float:
1103
+ // lib.asJavaDouble cannot be used here because it models PyFloat_AsDouble,
1104
+ // which ignores __float__ defined by float subclasses, whereas PyNumber_Float
1105
+ // uses the __float__ even for subclasses
1106
+ if (callFloatNode == null ) {
1107
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1108
+ callFloatNode = insert (LookupAndCallUnaryNode .create (__FLOAT__ ));
1109
+ }
1110
+ Object result = callFloatNode .executeObject (frame , obj );
1111
+ if (result != PNone .NO_VALUE ) {
1112
+ if (PGuards .isDouble (result )) {
1113
+ return (double ) result ;
1114
+ }
1115
+ if (PGuards .isPFloat (result )) {
1116
+ if (!isPrimitiveProfile .profileObject (result , PythonBuiltinClassType .PFloat )) {
1117
+ // TODO deprecation warning
1118
+ }
1119
+ return ((PFloat ) result ).getValue ();
1120
+ }
1121
+ throw raise (TypeError , ErrorMessages .RETURNED_NON_FLOAT , "__float__" , result );
1122
+ }
1123
+ if (lib .canBeIndex (obj )) {
1124
+ return lib .asJavaDouble (lib .asIndex (obj ));
1125
+ }
1126
+ // Follows logic from PyFloat_FromString:
1127
+ // These types are handled only if the object doesn't implement __float__/__index__
1128
+ if (obj instanceof PString ) {
1129
+ return convertStringToDouble (frame , ((PString ) obj ).getValue (), obj );
1058
1130
} else if (obj instanceof PIBytesLike ) {
1059
1131
return convertBytesToDouble (frame , (PIBytesLike ) obj );
1132
+ } else if (lib .isBuffer (obj )) {
1133
+ try {
1134
+ return convertStringToDouble (frame , createString (lib .getBufferBytes (obj )), obj );
1135
+ } catch (UnsupportedMessageException e ) {
1136
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1137
+ throw new IllegalStateException ("Object claims to be a buffer but does not support getBufferBytes()" );
1138
+ }
1060
1139
}
1061
- if (lib .canBeJavaDouble (obj )) {
1062
- return lib .asJavaDouble (obj );
1063
- } else {
1064
- throw raise (PythonBuiltinClassType .TypeError , ErrorMessages .ARG_MUST_BE_STRING_OR_NUMBER , "float()" , obj );
1065
- }
1140
+ throw raise (PythonBuiltinClassType .TypeError , ErrorMessages .ARG_MUST_BE_STRING_OR_NUMBER , "float()" , obj );
1066
1141
}
1067
1142
1068
- @ Specialization (guards = "!isNativeClass(cls)" )
1143
+ @ Specialization (guards = { "!isNativeClass(cls)" , "!isPrimitiveFloat(cls)" } )
1069
1144
Object doPythonObject (VirtualFrame frame , Object cls , Object obj ,
1070
- @ CachedLibrary (limit = "1" ) PythonObjectLibrary lib ) {
1071
- return floatFromDouble (cls , doubleFromObject (frame , cls , obj , lib ));
1145
+ @ Cached FloatNode recursiveCallNode ) {
1146
+ Object doubleValue = recursiveCallNode .executeWith (frame , PythonBuiltinClassType .PFloat , obj );
1147
+ if (!(doubleValue instanceof Double )) {
1148
+ throw CompilerDirectives .shouldNotReachHere ("float() returned non-primitive value" );
1149
+ }
1150
+ return floatFromDouble (cls , (double ) doubleValue );
1072
1151
}
1073
1152
1074
1153
// logic similar to float_subtype_new(PyTypeObject *type, PyObject *x) from CPython
@@ -1078,9 +1157,12 @@ Object doPythonObject(VirtualFrame frame, Object cls, Object obj,
1078
1157
Object doPythonObject (VirtualFrame frame , PythonNativeClass cls , Object obj ,
1079
1158
@ Cached @ SuppressWarnings ("unused" ) IsSubtypeNode isSubtype ,
1080
1159
@ Cached CExtNodes .FloatSubtypeNew subtypeNew ,
1081
- @ CachedLibrary (limit = "1" ) PythonObjectLibrary lib ) {
1082
- double realFloat = doubleFromObject (frame , PythonBuiltinClassType .PFloat , obj , lib );
1083
- return subtypeNew .call (cls , realFloat );
1160
+ @ Cached FloatNode recursiveCallNode ) {
1161
+ Object doubleValue = recursiveCallNode .executeWith (frame , PythonBuiltinClassType .PFloat , obj );
1162
+ if (!(doubleValue instanceof Double )) {
1163
+ throw CompilerDirectives .shouldNotReachHere ("float() returned non-primitive value" );
1164
+ }
1165
+ return subtypeNew .call (cls , (double ) doubleValue );
1084
1166
}
1085
1167
1086
1168
@ Fallback
0 commit comments