Skip to content

Commit 0e067ef

Browse files
nicowilliamsemanuele6
authored andcommitted
Improve handling of non-integer numeric indices (fix #2815)
1 parent ab47880 commit 0e067ef

File tree

2 files changed

+145
-49
lines changed

2 files changed

+145
-49
lines changed

src/jv_aux.c

Lines changed: 87 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#include <assert.h>
2+
#include <limits.h>
3+
#include <math.h>
14
#include <string.h>
25
#include <stdlib.h>
3-
#include <assert.h>
46
#include "jv_alloc.h"
57
#include "jv_private.h"
68

@@ -13,7 +15,7 @@ static double jv_number_get_value_and_consume(jv number) {
1315
return value;
1416
}
1517

16-
static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
18+
static jv parse_slice(jv j, jv slice, int* pstart, int* pend) {
1719
// Array slices
1820
jv start_jv = jv_object_get(jv_copy(slice), jv_string("start"));
1921
jv end_jv = jv_object_get(slice, jv_string("end"));
@@ -27,8 +29,14 @@ static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
2729
} else if (jv_get_kind(j) == JV_KIND_STRING) {
2830
len = jv_string_length_codepoints(j);
2931
} else {
32+
/*
33+
* XXX This should be dead code because callers shouldn't call this
34+
* function if `j' is neither an array nor a string.
35+
*/
3036
jv_free(j);
31-
return 0;
37+
jv_free(start_jv);
38+
jv_free(end_jv);
39+
return jv_invalid_with_msg(jv_string("Only arrays and strings can be sliced"));
3240
}
3341
if (jv_get_kind(end_jv) == JV_KIND_NULL) {
3442
jv_free(end_jv);
@@ -38,29 +46,34 @@ static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
3846
jv_get_kind(end_jv) != JV_KIND_NUMBER) {
3947
jv_free(start_jv);
4048
jv_free(end_jv);
41-
return 0;
42-
} else {
43-
double dstart = jv_number_value(start_jv);
44-
double dend = jv_number_value(end_jv);
45-
jv_free(start_jv);
46-
jv_free(end_jv);
47-
if (dstart < 0) dstart += len;
48-
if (dend < 0) dend += len;
49-
if (dstart < 0) dstart = 0;
50-
if (dstart > len) dstart = len;
51-
52-
int start = (int)dstart;
53-
int end = (dend > len) ? len : (int)dend;
54-
// Ends are exclusive but e.g. 1 < 1.5 so :1.5 should be :2 not :1
55-
if(end < dend) end += 1;
56-
57-
if (end > len) end = len;
58-
if (end < start) end = start;
59-
assert(0 <= start && start <= end && end <= len);
60-
*pstart = start;
61-
*pend = end;
62-
return 1;
63-
}
49+
return jv_invalid_with_msg(jv_string("Array/string slice indices must be integers"));
50+
}
51+
52+
double dstart = jv_number_value(start_jv);
53+
double dend = jv_number_value(end_jv);
54+
int start, end;
55+
56+
jv_free(start_jv);
57+
jv_free(end_jv);
58+
if (isnan(dstart)) dstart = 0;
59+
if (dstart < 0) dstart += len;
60+
if (dstart < 0) dstart = 0;
61+
if (dstart > len) dstart = len;
62+
start = dstart > INT_MAX ? INT_MAX : (int)dstart; // Rounds down
63+
64+
if (isnan(dend)) dend = len;
65+
if (dend < 0) dend += len;
66+
if (dend < 0) dend = start;
67+
end = dend > INT_MAX ? INT_MAX : (int)dend;
68+
if (end > len) end = len;
69+
if (end < len) end += end < dend ? 1 : 0; // We round start down
70+
// but round end up
71+
72+
if (end < start) end = start;
73+
assert(0 <= start && start <= end && end <= len);
74+
*pstart = start;
75+
*pend = end;
76+
return jv_true();
6477
}
6578

6679
jv jv_get(jv t, jv k) {
@@ -72,36 +85,44 @@ jv jv_get(jv t, jv k) {
7285
v = jv_null();
7386
}
7487
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
75-
if(jv_is_integer(k)){
76-
int idx = (int)jv_number_value(k);
77-
if (idx < 0)
78-
idx += jv_array_length(jv_copy(t));
79-
v = jv_array_get(t, idx);
80-
if (!jv_is_valid(v)) {
81-
jv_free(v);
82-
v = jv_null();
83-
}
84-
jv_free(k);
85-
} else {
88+
if (jvp_number_is_nan(k)) {
8689
jv_free(t);
87-
jv_free(k);
8890
v = jv_null();
91+
} else {
92+
double didx = jv_number_value(k);
93+
if (jvp_number_is_nan(k)) {
94+
v = jv_null();
95+
} else {
96+
if (didx < INT_MIN) didx = INT_MIN;
97+
if (didx > INT_MAX) didx = INT_MAX;
98+
int idx = (int)jv_number_value(k);
99+
if (idx < 0)
100+
idx += jv_array_length(jv_copy(t));
101+
v = jv_array_get(t, idx);
102+
if (!jv_is_valid(v)) {
103+
jv_free(v);
104+
v = jv_null();
105+
}
106+
}
89107
}
108+
jv_free(k);
90109
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) {
91110
int start, end;
92-
if (parse_slice(jv_copy(t), k, &start, &end)) {
111+
jv e = parse_slice(jv_copy(t), k, &start, &end);
112+
if (jv_get_kind(e) == JV_KIND_TRUE) {
93113
v = jv_array_slice(t, start, end);
94114
} else {
95115
jv_free(t);
96-
v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
116+
v = e;
97117
}
98118
} else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) {
99119
int start, end;
100-
if (parse_slice(jv_copy(t), k, &start, &end)) {
120+
jv e = parse_slice(jv_copy(t), k, &start, &end);
121+
if (jv_get_kind(e) == JV_KIND_TRUE) {
101122
v = jv_string_slice(t, start, end);
102123
} else {
103-
v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an string slice must be numbers"));
104124
jv_free(t);
125+
v = e;
105126
}
106127
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) {
107128
v = jv_array_indexes(t, k);
@@ -146,14 +167,24 @@ jv jv_set(jv t, jv k, jv v) {
146167
t = jv_object_set(t, k, v);
147168
} else if (jv_get_kind(k) == JV_KIND_NUMBER &&
148169
(jv_get_kind(t) == JV_KIND_ARRAY || isnull)) {
149-
if (isnull) t = jv_array();
150-
t = jv_array_set(t, (int)jv_number_value(k), v);
151-
jv_free(k);
170+
if (jvp_number_is_nan(k)) {
171+
jv_free(t);
172+
jv_free(k);
173+
t = jv_invalid_with_msg(jv_string("Cannot set array element at NaN index"));
174+
} else {
175+
double didx = jv_number_value(k);
176+
if (didx < INT_MIN) didx = INT_MIN;
177+
if (didx > INT_MAX) didx = INT_MAX;
178+
if (isnull) t = jv_array();
179+
t = jv_array_set(t, (int)didx, v);
180+
jv_free(k);
181+
}
152182
} else if (jv_get_kind(k) == JV_KIND_OBJECT &&
153183
(jv_get_kind(t) == JV_KIND_ARRAY || isnull)) {
154184
if (isnull) t = jv_array();
155185
int start, end;
156-
if (parse_slice(jv_copy(t), k, &start, &end)) {
186+
jv e = parse_slice(jv_copy(t), k, &start, &end);
187+
if (jv_get_kind(e) == JV_KIND_TRUE) {
157188
if (jv_get_kind(v) == JV_KIND_ARRAY) {
158189
int array_len = jv_array_length(jv_copy(t));
159190
assert(0 <= start && start <= end && end <= array_len);
@@ -185,8 +216,14 @@ jv jv_set(jv t, jv k, jv v) {
185216
} else {
186217
jv_free(t);
187218
jv_free(v);
188-
t = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
219+
t = e;
189220
}
221+
} else if (jv_get_kind(k) == JV_KIND_OBJECT && jv_get_kind(t) == JV_KIND_STRING) {
222+
jv_free(t);
223+
jv_free(k);
224+
jv_free(v);
225+
/* Well, why not? We should implement this... */
226+
t = jv_invalid_with_msg(jv_string_fmt("Cannot update string slices"));
190227
} else {
191228
jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s",
192229
jv_kind_name(jv_get_kind(k)),
@@ -255,13 +292,14 @@ static jv jv_dels(jv t, jv keys) {
255292
}
256293
} else if (jv_get_kind(key) == JV_KIND_OBJECT) {
257294
int start, end;
258-
if (parse_slice(jv_copy(t), key, &start, &end)) {
295+
jv e = parse_slice(jv_copy(t), key, &start, &end);
296+
if (jv_get_kind(e) == JV_KIND_TRUE) {
259297
starts = jv_array_append(starts, jv_number(start));
260298
ends = jv_array_append(ends, jv_number(end));
261299
} else {
262300
jv_free(new_array);
263301
jv_free(key);
264-
new_array = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
302+
new_array = e;
265303
goto arr_out;
266304
}
267305
} else {

tests/jq.test

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,3 +2024,61 @@ walk(1)
20242024
walk(select(IN({}, []) | not))
20252025
{"a":1,"b":[]}
20262026
{"a":1}
2027+
2028+
# #2815
2029+
[range(10)] | .[1.2:3.5]
2030+
null
2031+
[1,2,3]
2032+
2033+
[range(10)] | .[1.5:3.5]
2034+
null
2035+
[1,2,3]
2036+
2037+
[range(10)] | .[1.7:3.5]
2038+
null
2039+
[1,2,3]
2040+
2041+
[range(10)] | .[1.7:4294967295]
2042+
null
2043+
[1,2,3,4,5,6,7,8,9]
2044+
2045+
[range(10)] | .[1.7:-4294967296]
2046+
null
2047+
[]
2048+
2049+
[[range(10)] | .[1.1,1.5,1.7]]
2050+
null
2051+
[1,1,1]
2052+
2053+
[range(5)] | .[1.1] = 5
2054+
null
2055+
[0,5,2,3,4]
2056+
2057+
[range(3)] | .[nan:1]
2058+
null
2059+
[0]
2060+
2061+
[range(3)] | .[1:nan]
2062+
null
2063+
[1,2]
2064+
2065+
[range(3)] | .[nan]
2066+
null
2067+
null
2068+
2069+
try ([range(3)] | .[nan] = 9) catch .
2070+
null
2071+
"Cannot set array element at NaN index"
2072+
2073+
try ("foobar" | .[1.5:3.5] = "xyz") catch .
2074+
null
2075+
"Cannot update string slices"
2076+
2077+
try ([range(10)] | .[1.5:3.5] = ["xyz"]) catch .
2078+
null
2079+
[0,"xyz",4,5,6,7,8,9]
2080+
2081+
try ("foobar" | .[1.5]) catch .
2082+
null
2083+
"Cannot index string with number"
2084+

0 commit comments

Comments
 (0)