From 4a7976a5d89452be2d908ee6b4328e386a9b6064 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Fri, 6 Oct 2017 21:57:04 +0100 Subject: [PATCH] Fix platorm inconsistency in Python default argument handling. 32 bit and 64 bit compiled versions of SWIG generated different Python files when default arguments were outside the range of 32 bit signed integers. The default arguments specified in Python are now only those that are in the range of a 32 bit signed integer, otherwise the default is obtained from C/C++ code. Closes #1108 --- CHANGES.current | 7 +++++++ Examples/test-suite/default_args.i | 8 ++++++++ .../test-suite/python/default_args_runme.py | 18 ++++++++++++++++++ Source/Modules/python.cxx | 15 +++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/CHANGES.current b/CHANGES.current index 83518ed96..75e4c1fe2 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -6,6 +6,13 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.0.0 (in progress) =========================== +2017-10-06: wsfulton + [Python] Issue #1108. Fix platorm inconsistency in Python default argument handling. + 32 bit and 64 bit compiled versions of SWIG generated different Python files + when default arguments were outside the range of 32 bit signed integers. + The default arguments specified in Python are now only those that are in the + range of a 32 bit signed integer, otherwise the default is obtained from C/C++ code. + 2017-10-02: wsfulton [C#] Fix std::complex types passed by value. diff --git a/Examples/test-suite/default_args.i b/Examples/test-suite/default_args.i index 1f7183f47..1401cd576 100644 --- a/Examples/test-suite/default_args.i +++ b/Examples/test-suite/default_args.i @@ -28,6 +28,14 @@ int value_perm(int first, int mode = 0640 | 0004) { return mode; } int value_m01(int first, int val = -01) { return val; } bool booltest2(bool x = 0 | 1) { return x; } + int max_32bit_int1(int a = 0x7FFFFFFF) { return a; } + int max_32bit_int2(int a = 2147483647) { return a; } + int min_32bit_int1(int a = -0x80000000) { return a; } + int min_32bit_int2(int a = -2147483648) { return a; } + long long too_big_32bit_int1(long long a = 0x80000000) { return a; } + long long too_big_32bit_int2(long long a = 2147483648LL) { return a; } + long long too_small_32bit_int1(long long a = -0x80000001) { return a; } + long long too_small_32bit_int2(long long a = -2147483649LL) { return a; } }; void doublevalue1(int first, double num = 0.0e-1) {} diff --git a/Examples/test-suite/python/default_args_runme.py b/Examples/test-suite/python/default_args_runme.py index ddaf2cd4f..d745cae10 100644 --- a/Examples/test-suite/python/default_args_runme.py +++ b/Examples/test-suite/python/default_args_runme.py @@ -139,6 +139,24 @@ def run(module_name): print "booltest2 failed" tricky_failure = True + if tricky.max_32bit_int1() != 0x7FFFFFFF: + print "max_32bit_int1 failed" + tricky_failure = True + if tricky.min_32bit_int1() != -2147483648: + print "min_32bit_int1 failed" + tricky_failure = True + if tricky.max_32bit_int2() != 0x7FFFFFFF: + print "max_32bit_int2 failed" + tricky_failure = True + if tricky.min_32bit_int2() != -2147483648: + print "min_32bit_int2 failed" + tricky_failure = True + + tricky.too_big_32bit_int1() + tricky.too_small_32bit_int1() + tricky.too_big_32bit_int2() + tricky.too_small_32bit_int2() + if tricky_failure: raise RuntimeError diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index 259d7c0d8..58b61c97f 100644 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -18,9 +18,15 @@ #include #include +#include +#include + #define PYSHADOW_MEMBER 0x2 #define WARN_PYTHON_MULTIPLE_INH 405 +#define PYTHON_INT_MAX (2147483647) +#define PYTHON_INT_MIN (-2147483647-1) + static String *const_code = 0; static String *module = 0; static String *package = 0; @@ -2048,6 +2054,7 @@ public: long value = strtol(s, &end, 0); if (errno == ERANGE || end == s) return NIL; + if (*end != '\0') { // If there is a suffix after the number, we can safely ignore "l" // and (provided the number is unsigned) "u", and also combinations of @@ -2070,6 +2077,14 @@ public: // So now we are certain that we are indeed dealing with an integer // that has a representation as long given by value. + // Restrict to guaranteed supported range in Python, see maxint docs: https://docs.python.org/2/library/sys.html#sys.maxint + // Don't do this pointless check when long is 32 bits or smaller as strtol will have already failed with ERANGE +#if LONG_MAX > PYTHON_INT_MAX || LONG_MIN < PYTHON_INT_MIN + if (value > PYTHON_INT_MAX || value < PYTHON_INT_MIN) { + return NIL; + } +#endif + if (Cmp(resolved_type, "bool") == 0) // Allow integers as the default value for a bool parameter. return NewString(value ? "True" : "False");