1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 #include "js/Array.h" // JS::IsArrayObject
12 #include "js/Exception.h"
13 #include "js/friend/ErrorMessages.h" // JSMSG_*
15 #include "js/MemoryFunctions.h"
16 #include "js/Printf.h"
17 #include "js/PropertyAndElement.h" // JS_GetProperty
18 #include "jsapi-tests/tests.h"
22 class AutoInflatedString
{
28 explicit AutoInflatedString(JSContext
* cx
)
29 : cx(cx
), chars_(nullptr), length_(0) {}
30 ~AutoInflatedString() { JS_free(cx
, chars_
); }
33 void operator=(const char (&str
)[N
]) {
35 chars_
= InflateString(cx
, str
, length_
);
41 void operator=(const char* str
) {
42 length_
= strlen(str
);
43 chars_
= InflateString(cx
, str
, length_
);
49 const char16_t
* chars() const { return chars_
; }
50 size_t length() const { return length_
; }
53 BEGIN_TEST(testParseJSON_success
) {
55 JS::RootedValue
expected(cx
);
56 expected
= JS::TrueValue();
57 CHECK(TryParse(cx
, "true", expected
));
59 expected
= JS::FalseValue();
60 CHECK(TryParse(cx
, "false", expected
));
62 expected
= JS::NullValue();
63 CHECK(TryParse(cx
, "null", expected
));
66 CHECK(TryParse(cx
, "0", expected
));
69 CHECK(TryParse(cx
, "1", expected
));
71 expected
.setInt32(-1);
72 CHECK(TryParse(cx
, "-1", expected
));
74 expected
.setDouble(1);
75 CHECK(TryParse(cx
, "1", expected
));
77 expected
.setDouble(1.75);
78 CHECK(TryParse(cx
, "1.75", expected
));
80 expected
.setDouble(9e9
);
81 CHECK(TryParse(cx
, "9e9", expected
));
83 expected
.setDouble(std::numeric_limits
<double>::infinity());
84 CHECK(TryParse(cx
, "9e99999", expected
));
86 JS::Rooted
<JSLinearString
*> str(cx
);
88 const char16_t emptystr
[] = {'\0'};
89 str
= js::NewStringCopyN
<CanGC
>(cx
, emptystr
, 0);
91 expected
= JS::StringValue(str
);
92 CHECK(TryParse(cx
, "\"\"", expected
));
94 const char16_t nullstr
[] = {'\0'};
95 str
= NewString(cx
, nullstr
);
97 expected
= JS::StringValue(str
);
98 CHECK(TryParse(cx
, "\"\\u0000\"", expected
));
100 const char16_t backstr
[] = {'\b'};
101 str
= NewString(cx
, backstr
);
103 expected
= JS::StringValue(str
);
104 CHECK(TryParse(cx
, "\"\\b\"", expected
));
105 CHECK(TryParse(cx
, "\"\\u0008\"", expected
));
107 const char16_t newlinestr
[] = {
110 str
= NewString(cx
, newlinestr
);
112 expected
= JS::StringValue(str
);
113 CHECK(TryParse(cx
, "\"\\n\"", expected
));
114 CHECK(TryParse(cx
, "\"\\u000A\"", expected
));
117 JS::RootedValue
v(cx
), v2(cx
);
118 JS::RootedObject
obj(cx
);
122 CHECK(Parse(cx
, "[]", &v
));
125 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
127 CHECK(JS_GetProperty(cx
, obj
, "length", &v2
));
128 CHECK(v2
.isInt32(0));
130 CHECK(Parse(cx
, "[1]", &v
));
133 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
135 CHECK(JS_GetProperty(cx
, obj
, "0", &v2
));
136 CHECK(v2
.isInt32(1));
137 CHECK(JS_GetProperty(cx
, obj
, "length", &v2
));
138 CHECK(v2
.isInt32(1));
141 CHECK(Parse(cx
, "{}", &v
));
144 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
147 CHECK(Parse(cx
, "{ \"f\": 17 }", &v
));
150 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
152 CHECK(JS_GetProperty(cx
, obj
, "f", &v2
));
153 CHECK(v2
.isInt32(17));
159 static JSLinearString
* NewString(JSContext
* cx
, const char16_t (&chars
)[N
]) {
160 return js::NewStringCopyN
<CanGC
>(cx
, chars
, N
);
164 inline bool Parse(JSContext
* cx
, const char (&input
)[N
],
165 JS::MutableHandleValue vp
) {
166 AutoInflatedString
str(cx
);
168 CHECK(JS_ParseJSON(cx
, str
.chars(), str
.length(), vp
));
173 inline bool TryParse(JSContext
* cx
, const char (&input
)[N
],
174 JS::HandleValue expected
) {
175 AutoInflatedString
str(cx
);
178 CHECK(JS_ParseJSON(cx
, str
.chars(), str
.length(), &v
));
179 CHECK_SAME(v
, expected
);
182 END_TEST(testParseJSON_success
)
184 BEGIN_TEST(testParseJSON_error
) {
185 CHECK(Error(cx
, "", 1, 1));
186 CHECK(Error(cx
, "\n", 2, 1));
187 CHECK(Error(cx
, "\r", 2, 1));
188 CHECK(Error(cx
, "\r\n", 2, 1));
190 CHECK(Error(cx
, "[", 1, 2));
191 CHECK(Error(cx
, "[,]", 1, 2));
192 CHECK(Error(cx
, "[1,]", 1, 4));
193 CHECK(Error(cx
, "{a:2}", 1, 2));
194 CHECK(Error(cx
, "{\"a\":2,}", 1, 8));
195 CHECK(Error(cx
, "]", 1, 1));
196 CHECK(Error(cx
, "\"", 1, 2));
197 CHECK(Error(cx
, "{]", 1, 2));
198 CHECK(Error(cx
, "[}", 1, 2));
199 CHECK(Error(cx
, "'wrongly-quoted string'", 1, 1));
201 CHECK(Error(cx
, "{\"a\":2 \n b:3}", 2, 2));
202 CHECK(Error(cx
, "\n[", 2, 2));
203 CHECK(Error(cx
, "\n[,]", 2, 2));
204 CHECK(Error(cx
, "\n[1,]", 2, 4));
205 CHECK(Error(cx
, "\n{a:2}", 2, 2));
206 CHECK(Error(cx
, "\n{\"a\":2,}", 2, 8));
207 CHECK(Error(cx
, "\n]", 2, 1));
208 CHECK(Error(cx
, "\"bad string\n\"", 1, 12));
209 CHECK(Error(cx
, "\r'wrongly-quoted string'", 2, 1));
210 CHECK(Error(cx
, "\n\"", 2, 2));
211 CHECK(Error(cx
, "\n{]", 2, 2));
212 CHECK(Error(cx
, "\n[}", 2, 2));
213 CHECK(Error(cx
, "{\"a\":[2,3],\n\"b\":,5,6}", 2, 5));
215 CHECK(Error(cx
, "{\"a\":2 \r b:3}", 2, 2));
216 CHECK(Error(cx
, "\r[", 2, 2));
217 CHECK(Error(cx
, "\r[,]", 2, 2));
218 CHECK(Error(cx
, "\r[1,]", 2, 4));
219 CHECK(Error(cx
, "\r{a:2}", 2, 2));
220 CHECK(Error(cx
, "\r{\"a\":2,}", 2, 8));
221 CHECK(Error(cx
, "\r]", 2, 1));
222 CHECK(Error(cx
, "\"bad string\r\"", 1, 12));
223 CHECK(Error(cx
, "\r'wrongly-quoted string'", 2, 1));
224 CHECK(Error(cx
, "\r\"", 2, 2));
225 CHECK(Error(cx
, "\r{]", 2, 2));
226 CHECK(Error(cx
, "\r[}", 2, 2));
227 CHECK(Error(cx
, "{\"a\":[2,3],\r\"b\":,5,6}", 2, 5));
229 CHECK(Error(cx
, "{\"a\":2 \r\n b:3}", 2, 2));
230 CHECK(Error(cx
, "\r\n[", 2, 2));
231 CHECK(Error(cx
, "\r\n[,]", 2, 2));
232 CHECK(Error(cx
, "\r\n[1,]", 2, 4));
233 CHECK(Error(cx
, "\r\n{a:2}", 2, 2));
234 CHECK(Error(cx
, "\r\n{\"a\":2,}", 2, 8));
235 CHECK(Error(cx
, "\r\n]", 2, 1));
236 CHECK(Error(cx
, "\"bad string\r\n\"", 1, 12));
237 CHECK(Error(cx
, "\r\n'wrongly-quoted string'", 2, 1));
238 CHECK(Error(cx
, "\r\n\"", 2, 2));
239 CHECK(Error(cx
, "\r\n{]", 2, 2));
240 CHECK(Error(cx
, "\r\n[}", 2, 2));
241 CHECK(Error(cx
, "{\"a\":[2,3],\r\n\"b\":,5,6}", 2, 5));
243 CHECK(Error(cx
, "\n\"bad string\n\"", 2, 12));
244 CHECK(Error(cx
, "\r\"bad string\r\"", 2, 12));
245 CHECK(Error(cx
, "\r\n\"bad string\r\n\"", 2, 12));
247 CHECK(Error(cx
, "{\n\"a\":[2,3],\r\"b\":,5,6}", 3, 5));
248 CHECK(Error(cx
, "{\r\"a\":[2,3],\n\"b\":,5,6}", 3, 5));
249 CHECK(Error(cx
, "[\"\\t\\q", 1, 6));
250 CHECK(Error(cx
, "[\"\\t\x00", 1, 5));
251 CHECK(Error(cx
, "[\"\\t\x01", 1, 5));
252 CHECK(Error(cx
, "[\"\\t\\\x00", 1, 6));
253 CHECK(Error(cx
, "[\"\\t\\\x01", 1, 6));
255 // Unicode escape errors are messy. The first bad character could be
256 // non-hexadecimal, or it could be absent entirely. Include tests where
257 // there's a bad character, followed by zero to as many characters as are
258 // needed to form a complete Unicode escape sequence, plus one. (The extra
259 // characters beyond are valuable because our implementation checks for
260 // too-few subsequent characters first, before checking for subsequent
261 // non-hexadecimal characters. So \u<END>, \u0<END>, \u00<END>, and
262 // \u000<END> are all *detected* as invalid by the same code path, but the
263 // process of computing the first invalid character follows a different
264 // code path for each. And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected
265 // as invalid by the same code path [ignoring which precise subexpression
266 // triggers failure of a single condition], but the computation of the
267 // first invalid character follows a different code path for each.)
268 CHECK(Error(cx
, "[\"\\t\\u", 1, 7));
269 CHECK(Error(cx
, "[\"\\t\\uZ", 1, 7));
270 CHECK(Error(cx
, "[\"\\t\\uZZ", 1, 7));
271 CHECK(Error(cx
, "[\"\\t\\uZZZ", 1, 7));
272 CHECK(Error(cx
, "[\"\\t\\uZZZZ", 1, 7));
273 CHECK(Error(cx
, "[\"\\t\\uZZZZZ", 1, 7));
275 CHECK(Error(cx
, "[\"\\t\\u0", 1, 8));
276 CHECK(Error(cx
, "[\"\\t\\u0Z", 1, 8));
277 CHECK(Error(cx
, "[\"\\t\\u0ZZ", 1, 8));
278 CHECK(Error(cx
, "[\"\\t\\u0ZZZ", 1, 8));
279 CHECK(Error(cx
, "[\"\\t\\u0ZZZZ", 1, 8));
281 CHECK(Error(cx
, "[\"\\t\\u00", 1, 9));
282 CHECK(Error(cx
, "[\"\\t\\u00Z", 1, 9));
283 CHECK(Error(cx
, "[\"\\t\\u00ZZ", 1, 9));
284 CHECK(Error(cx
, "[\"\\t\\u00ZZZ", 1, 9));
286 CHECK(Error(cx
, "[\"\\t\\u000", 1, 10));
287 CHECK(Error(cx
, "[\"\\t\\u000Z", 1, 10));
288 CHECK(Error(cx
, "[\"\\t\\u000ZZ", 1, 10));
294 inline bool Error(JSContext
* cx
, const char (&input
)[N
], uint32_t expectedLine
,
295 uint32_t expectedColumn
) {
296 AutoInflatedString
str(cx
);
297 RootedValue
dummy(cx
);
300 bool ok
= JS_ParseJSON(cx
, str
.chars(), str
.length(), &dummy
);
303 JS::ExceptionStack
exnStack(cx
);
304 CHECK(StealPendingExceptionStack(cx
, &exnStack
));
306 JS::ErrorReportBuilder
report(cx
);
307 CHECK(report
.init(cx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
));
308 CHECK(report
.report()->errorNumber
== JSMSG_JSON_BAD_PARSE
);
310 UniqueChars lineAndColumnASCII
=
311 JS_smprintf("line %d column %d", expectedLine
, expectedColumn
);
312 CHECK(strstr(report
.toStringResult().c_str(), lineAndColumnASCII
.get()) !=
315 /* We do not execute JS, so there should be no exception thrown. */
316 CHECK(!JS_IsExceptionPending(cx
));
320 END_TEST(testParseJSON_error
)
322 static bool Censor(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
323 JS::CallArgs args
= JS::CallArgsFromVp(argc
, vp
);
324 MOZ_RELEASE_ASSERT(args
.length() == 2);
325 MOZ_RELEASE_ASSERT(args
[0].isString());
326 args
.rval().setNull();
330 BEGIN_TEST(testParseJSON_reviver
) {
331 JSFunction
* fun
= JS_NewFunction(cx
, Censor
, 0, 0, "censor");
334 JS::RootedValue
filter(cx
, JS::ObjectValue(*JS_GetFunctionObject(fun
)));
336 CHECK(TryParse(cx
, "true", filter
));
337 CHECK(TryParse(cx
, "false", filter
));
338 CHECK(TryParse(cx
, "null", filter
));
339 CHECK(TryParse(cx
, "1", filter
));
340 CHECK(TryParse(cx
, "1.75", filter
));
341 CHECK(TryParse(cx
, "[]", filter
));
342 CHECK(TryParse(cx
, "[1]", filter
));
343 CHECK(TryParse(cx
, "{}", filter
));
348 inline bool TryParse(JSContext
* cx
, const char (&input
)[N
],
349 JS::HandleValue filter
) {
350 AutoInflatedString
str(cx
);
351 JS::RootedValue
v(cx
);
353 CHECK(JS_ParseJSONWithReviver(cx
, str
.chars(), str
.length(), filter
, &v
));
357 END_TEST(testParseJSON_reviver
)