diff --git a/docs/notes/bugfix-21476.md b/docs/notes/bugfix-21476.md new file mode 100644 index 00000000000..89bcac7782c --- /dev/null +++ b/docs/notes/bugfix-21476.md @@ -0,0 +1 @@ +# Ensure "the extents of tArray" returns empty if the keys of tArray are not integers diff --git a/engine/src/exec-array.cpp b/engine/src/exec-array.cpp index d286dfbff3e..c9aac46bd9d 100644 --- a/engine/src/exec-array.cpp +++ b/engine/src/exec-array.cpp @@ -970,7 +970,8 @@ bool MCArraysSplitIndexes(MCNameRef p_key, integer_t*& r_indexes, uindex_t& r_co return false; MCAutoNumberRef t_number; - if (!MCNumberParse(*t_substring, &t_number)) + + if (!MCNumberParseInteger(*t_substring, &t_number)) { if (!t_indexes . Push(0)) return false; @@ -978,7 +979,7 @@ bool MCArraysSplitIndexes(MCNameRef p_key, integer_t*& r_indexes, uindex_t& r_co r_all_integers = false; break; } - + if (!t_indexes . Push(MCNumberFetchAsInteger(*t_number))) return false; diff --git a/libfoundation/include/foundation.h b/libfoundation/include/foundation.h index c1a8e7f1219..e7855a13f98 100755 --- a/libfoundation/include/foundation.h +++ b/libfoundation/include/foundation.h @@ -2053,6 +2053,7 @@ MC_DLLEXPORT bool MCNumberParseOffsetPartial(MCStringRef p_string, uindex_t offs MC_DLLEXPORT bool MCNumberParseOffset(MCStringRef p_string, uindex_t offset, uindex_t char_count, MCNumberRef &r_number); MC_DLLEXPORT bool MCNumberParse(MCStringRef string, MCNumberRef& r_number); +MC_DLLEXPORT bool MCNumberParseInteger(MCStringRef string, MCNumberRef& r_number); MC_DLLEXPORT bool MCNumberParseUnicodeChars(const unichar_t *chars, uindex_t char_count, MCNumberRef& r_number); #if defined(__HAS_CORE_FOUNDATION__) diff --git a/libfoundation/src/foundation-number.cpp b/libfoundation/src/foundation-number.cpp index 65ceb1b8d39..671260ec8cf 100644 --- a/libfoundation/src/foundation-number.cpp +++ b/libfoundation/src/foundation-number.cpp @@ -23,6 +23,14 @@ along with LiveCode. If not see . */ //////////////////////////////////////////////////////////////////////////////// +// Forward declarations of internal utility functions + +static bool __MCNumberParseNativeString(const char *p_string, uindex_t p_length, bool p_full_string, bool p_integer_only, uindex_t &r_length_used, MCNumberRef &r_number); +static bool __MCNumberParseOffset(MCStringRef p_string, uindex_t offset, uindex_t char_count, bool p_integer_only, MCNumberRef &r_number); +static bool __MCNumberParseUnicodeChars(const unichar_t *p_chars, uindex_t p_char_count, bool p_integer_only, MCNumberRef& r_number); + +//////////////////////////////////////////////////////////////////////////////// + MC_DLLEXPORT_DEF bool MCNumberCreateWithInteger(integer_t p_value, MCNumberRef& r_number) { @@ -126,31 +134,101 @@ compare_t MCNumberCompareTo(MCNumberRef self, MCNumberRef p_other_self) return 0; } -bool __MCNumberParseNativeString(const char *p_string, uindex_t p_length, bool p_full_string, uindex_t &r_length_used, MCNumberRef &r_number) +///////////////////////////////////////////////////////////////////////////////// + +MC_DLLEXPORT_DEF +bool MCNumberParseOffset(MCStringRef p_string, uindex_t offset, uindex_t char_count, MCNumberRef &r_number) +{ + return __MCNumberParseOffset(p_string, offset, char_count, false, r_number); +} + +MC_DLLEXPORT_DEF +bool MCNumberParse(MCStringRef p_string, MCNumberRef &r_number) +{ + return __MCNumberParseOffset(p_string, 0, MCStringGetLength(p_string), false, r_number); +} + +MC_DLLEXPORT_DEF +bool MCNumberParseInteger(MCStringRef p_string, MCNumberRef &r_number) +{ + return __MCNumberParseOffset(p_string, 0, MCStringGetLength(p_string), true, r_number); +} + +MC_DLLEXPORT_DEF +bool MCNumberParseUnicodeChars(const unichar_t *p_chars, uindex_t p_char_count, MCNumberRef& r_number) +{ + return __MCNumberParseUnicodeChars(p_chars, p_char_count, false, r_number); +} + +MC_DLLEXPORT_DEF +bool MCNumberParseOffsetPartial(MCStringRef p_string, uindex_t offset, uindex_t &r_chars_used, MCNumberRef &r_number) { bool t_success; t_success = true; - MCNumberRef t_number; - t_number = nil; + char *t_buffer; + t_buffer = nil; + + const char *t_native_string; + t_native_string = nil; + + uindex_t t_length; + t_length = MCStringGetLength(p_string); + + if (offset > t_length) + offset = t_length; + if (MCStringIsNative(p_string)) + t_native_string = (const char*)MCStringGetNativeCharPtr(p_string) + offset; + else + { + t_success = MCMemoryNewArray(t_length - offset + 1, t_buffer); + + uindex_t t_native_char_count; + if (t_success) + t_success = MCUnicodeCharsMapToNative(MCStringGetCharPtr(p_string) + offset, t_length - offset, (char_t*)t_buffer, t_native_char_count, '?'); + + t_native_string = t_buffer; + } + + if (t_success) + t_success = __MCNumberParseNativeString(t_native_string, t_length - offset, false, false, r_chars_used, r_number); + + MCMemoryDeleteArray(t_buffer); + + return t_success; +} + +//////////////////////////////////////////////////////////////////////////////// + +static bool __MCNumberParseNativeString(const char *p_string, uindex_t p_length, bool p_full_string, bool p_integer_only, uindex_t &r_length_used, MCNumberRef &r_number) +{ + bool t_success; + t_success = true; + + MCNumberRef t_number; + t_number = nil; + uinteger_t t_base; t_base = 10; const char *t_string; t_string = p_string; - if (p_length > 2 && - p_string[0] == '0' && - (p_string[1] == 'x' || p_string[1] == 'X')) - { - // If the string begins with 0x then parse as hex, and discard first two chars - t_base = 16; - t_string += 2; + if (!p_integer_only) + { + if (p_length > 2 && + p_string[0] == '0' && + (p_string[1] == 'x' || p_string[1] == 'X')) + { + // If the string begins with 0x then parse as hex, and discard first two chars + t_base = 16; + t_string += 2; + } } errno = 0; - + char *t_end; t_end = nil; @@ -166,13 +244,17 @@ bool __MCNumberParseNativeString(const char *p_string, uindex_t p_length, bool p #endif t_success = (errno != ERANGE) && (p_full_string ? (t_end - p_string == (ptrdiff_t)p_length) : (t_end != t_string)); + + if (!t_success && p_integer_only) + return false; + if (t_success) t_success = MCNumberCreateWithInteger(t_integer, t_number); // If parsing as base 10 unsigned integer failed, try to parse as real. else if (t_base == 10) { errno = 0; - + real64_t t_real; t_real = strtod(p_string, &t_end); @@ -181,18 +263,17 @@ bool __MCNumberParseNativeString(const char *p_string, uindex_t p_length, bool p if (t_success) t_success = MCNumberCreateWithReal(t_real, t_number); } - - if (t_success) - { - r_number = t_number; - r_length_used = t_end - p_string; - } - - return t_success; + + if (t_success) + { + r_number = t_number; + r_length_used = t_end - p_string; + } + + return t_success; } -MC_DLLEXPORT_DEF -bool MCNumberParseOffset(MCStringRef p_string, uindex_t offset, uindex_t char_count, MCNumberRef &r_number) +static bool __MCNumberParseOffset(MCStringRef p_string, uindex_t offset, uindex_t char_count, bool p_integer_only, MCNumberRef &r_number) { uindex_t length = MCStringGetLength(p_string); if (offset > length) @@ -207,83 +288,37 @@ bool MCNumberParseOffset(MCStringRef p_string, uindex_t offset, uindex_t char_co bool t_success; t_success = false; - uindex_t t_length_used; - t_length_used = 0; - - t_success = __MCNumberParseNativeString((const char*)MCStringGetNativeCharPtr(p_string) + offset, char_count, true, t_length_used, r_number); - - return t_success; -} - -MC_DLLEXPORT_DEF -bool MCNumberParse(MCStringRef p_string, MCNumberRef &r_number) -{ - return MCNumberParseOffset(p_string, 0, MCStringGetLength(p_string), r_number); + uindex_t t_length_used; + t_length_used = 0; + + t_success = __MCNumberParseNativeString((const char*)MCStringGetNativeCharPtr(p_string) + offset, char_count, true, p_integer_only, t_length_used, r_number); + + return t_success; } -MC_DLLEXPORT_DEF -bool MCNumberParseUnicodeChars(const unichar_t *p_chars, uindex_t p_char_count, MCNumberRef& r_number) +static bool __MCNumberParseUnicodeChars(const unichar_t *p_chars, uindex_t p_char_count, bool p_integer_only, MCNumberRef& r_number) { - char *t_native_chars; - if (!MCMemoryNewArray(p_char_count + 1, t_native_chars)) - return false; + char *t_native_chars; + if (!MCMemoryNewArray(p_char_count + 1, t_native_chars)) + return false; - uindex_t t_native_char_count; - MCUnicodeCharsMapToNative(p_chars, p_char_count, (char_t *)t_native_chars, t_native_char_count, '?'); + uindex_t t_native_char_count; + MCUnicodeCharsMapToNative(p_chars, p_char_count, (char_t *)t_native_chars, t_native_char_count, '?'); - bool t_success; - t_success = false; - - uindex_t t_length_used; - t_length_used = 0; - - t_success = __MCNumberParseNativeString(t_native_chars, p_char_count, true, t_length_used, r_number); + bool t_success; + t_success = false; - MCMemoryDeleteArray(t_native_chars); + uindex_t t_length_used; + t_length_used = 0; - return t_success; -} - -MC_DLLEXPORT_DEF -bool MCNumberParseOffsetPartial(MCStringRef p_string, uindex_t offset, uindex_t &r_chars_used, MCNumberRef &r_number) -{ - bool t_success; - t_success = true; - - char *t_buffer; - t_buffer = nil; - - const char *t_native_string; - t_native_string = nil; - - uindex_t t_length; - t_length = MCStringGetLength(p_string); - - if (offset > t_length) - offset = t_length; - - if (MCStringIsNative(p_string)) - t_native_string = (const char*)MCStringGetNativeCharPtr(p_string) + offset; - else - { - t_success = MCMemoryNewArray(t_length - offset + 1, t_buffer); - - uindex_t t_native_char_count; - if (t_success) - t_success = MCUnicodeCharsMapToNative(MCStringGetCharPtr(p_string) + offset, t_length - offset, (char_t*)t_buffer, t_native_char_count, '?'); - - t_native_string = t_buffer; - } - - if (t_success) - t_success = __MCNumberParseNativeString(t_native_string, t_length - offset, false, r_chars_used, r_number); - - MCMemoryDeleteArray(t_buffer); - - return t_success; + t_success = __MCNumberParseNativeString(t_native_chars, p_char_count, true, p_integer_only, t_length_used, r_number); + + MCMemoryDeleteArray(t_native_chars); + + return t_success; } -//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// bool __MCNumberCopyDescription(__MCNumber *self, MCStringRef& r_string) { diff --git a/tests/lcs/core/array/array.livecodescript b/tests/lcs/core/array/array.livecodescript index 9721efab32e..a597ba9de2a 100644 --- a/tests/lcs/core/array/array.livecodescript +++ b/tests/lcs/core/array/array.livecodescript @@ -113,6 +113,25 @@ on TestExtents TestAssert "Extents of dimensional arrays", the extents of tDimensionalArray is tExpectedExtents end TestExtents +on TestExtentsDecimal + local tArray + repeat with i=1 to 5 + put "true" into tArray[i] + end repeat + put "false" into tArray[1.0] + + TestAssert "Extents of array with a decimal key", the extents of tArray is empty + + delete variable tArray[1.0] + local tSequenceKeys + put __keysForArray(kSequenceArray) into tSequenceKeys + TestAssert "Extents of array without a decimal key", the extents of tArray is (item 1 of tSequenceKeys, item -1 of tSequenceKeys) + + put "false" into tArray[pi] + TestAssert "Extents of array with another decimal key", the extents of tArray is empty + +end TestExtentsDecimal + on __testIsAmong pUseIsAmong local tArray, tKeyList, tKeys