1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 */
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/. */
7
8 #include <limits>
9 #include <string.h>
10
11 #include "jsprf.h"
12 #include "jsstr.h"
13
14 #include "jsapi-tests/tests.h"
15
16 using namespace js;
17
18 class AutoInflatedString {
19 JSContext * const cx;
20 char16_t* chars_;
21 size_t length_;
22
23 public:
AutoInflatedString(JSContext * cx)24 explicit AutoInflatedString(JSContext* cx) : cx(cx), chars_(nullptr), length_(0) { }
~AutoInflatedString()25 ~AutoInflatedString() {
26 JS_free(cx, chars_);
27 }
28
operator =(const char (& str)[N])29 template<size_t N> void operator=(const char (&str)[N]) {
30 length_ = N - 1;
31 chars_ = InflateString(cx, str, &length_);
32 if (!chars_)
33 abort();
34 }
35
operator =(const char * str)36 void operator=(const char* str) {
37 length_ = strlen(str);
38 chars_ = InflateString(cx, str, &length_);
39 if (!chars_)
40 abort();
41 }
42
chars() const43 const char16_t* chars() const { return chars_; }
length() const44 size_t length() const { return length_; }
45 };
46
BEGIN_TEST(testParseJSON_success)47 BEGIN_TEST(testParseJSON_success)
48 {
49 // Primitives
50 JS::RootedValue expected(cx);
51 expected = JS::TrueValue();
52 CHECK(TryParse(cx, "true", expected));
53
54 expected = JS::FalseValue();
55 CHECK(TryParse(cx, "false", expected));
56
57 expected = JS::NullValue();
58 CHECK(TryParse(cx, "null", expected));
59
60 expected.setInt32(0);
61 CHECK(TryParse(cx, "0", expected));
62
63 expected.setInt32(1);
64 CHECK(TryParse(cx, "1", expected));
65
66 expected.setInt32(-1);
67 CHECK(TryParse(cx, "-1", expected));
68
69 expected.setDouble(1);
70 CHECK(TryParse(cx, "1", expected));
71
72 expected.setDouble(1.75);
73 CHECK(TryParse(cx, "1.75", expected));
74
75 expected.setDouble(9e9);
76 CHECK(TryParse(cx, "9e9", expected));
77
78 expected.setDouble(std::numeric_limits<double>::infinity());
79 CHECK(TryParse(cx, "9e99999", expected));
80
81 JS::Rooted<JSFlatString*> str(cx);
82
83 const char16_t emptystr[] = { '\0' };
84 str = js::NewStringCopyN<CanGC>(cx, emptystr, 0);
85 CHECK(str);
86 expected = JS::StringValue(str);
87 CHECK(TryParse(cx, "\"\"", expected));
88
89 const char16_t nullstr[] = { '\0' };
90 str = NewString(cx, nullstr);
91 CHECK(str);
92 expected = JS::StringValue(str);
93 CHECK(TryParse(cx, "\"\\u0000\"", expected));
94
95 const char16_t backstr[] = { '\b' };
96 str = NewString(cx, backstr);
97 CHECK(str);
98 expected = JS::StringValue(str);
99 CHECK(TryParse(cx, "\"\\b\"", expected));
100 CHECK(TryParse(cx, "\"\\u0008\"", expected));
101
102 const char16_t newlinestr[] = { '\n', };
103 str = NewString(cx, newlinestr);
104 CHECK(str);
105 expected = JS::StringValue(str);
106 CHECK(TryParse(cx, "\"\\n\"", expected));
107 CHECK(TryParse(cx, "\"\\u000A\"", expected));
108
109
110 // Arrays
111 JS::RootedValue v(cx), v2(cx);
112 JS::RootedObject obj(cx);
113
114 bool isArray;
115
116 CHECK(Parse(cx, "[]", &v));
117 CHECK(v.isObject());
118 obj = &v.toObject();
119 CHECK(JS_IsArrayObject(cx, obj, &isArray));
120 CHECK(isArray);
121 CHECK(JS_GetProperty(cx, obj, "length", &v2));
122 CHECK(v2.isInt32(0));
123
124 CHECK(Parse(cx, "[1]", &v));
125 CHECK(v.isObject());
126 obj = &v.toObject();
127 CHECK(JS_IsArrayObject(cx, obj, &isArray));
128 CHECK(isArray);
129 CHECK(JS_GetProperty(cx, obj, "0", &v2));
130 CHECK(v2.isInt32(1));
131 CHECK(JS_GetProperty(cx, obj, "length", &v2));
132 CHECK(v2.isInt32(1));
133
134
135 // Objects
136 CHECK(Parse(cx, "{}", &v));
137 CHECK(v.isObject());
138 obj = &v.toObject();
139 CHECK(JS_IsArrayObject(cx, obj, &isArray));
140 CHECK(!isArray);
141
142 CHECK(Parse(cx, "{ \"f\": 17 }", &v));
143 CHECK(v.isObject());
144 obj = &v.toObject();
145 CHECK(JS_IsArrayObject(cx, obj, &isArray));
146 CHECK(!isArray);
147 CHECK(JS_GetProperty(cx, obj, "f", &v2));
148 CHECK(v2.isInt32(17));
149
150 return true;
151 }
152
153 template<size_t N> static JSFlatString*
NewString(JSContext * cx,const char16_t (& chars)[N])154 NewString(JSContext* cx, const char16_t (&chars)[N])
155 {
156 return js::NewStringCopyN<CanGC>(cx, chars, N);
157 }
158
159 template<size_t N> inline bool
Parse(JSContext * cx,const char (& input)[N],JS::MutableHandleValue vp)160 Parse(JSContext* cx, const char (&input)[N], JS::MutableHandleValue vp)
161 {
162 AutoInflatedString str(cx);
163 str = input;
164 CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp));
165 return true;
166 }
167
168 template<size_t N> inline bool
TryParse(JSContext * cx,const char (& input)[N],JS::HandleValue expected)169 TryParse(JSContext* cx, const char (&input)[N], JS::HandleValue expected)
170 {
171 AutoInflatedString str(cx);
172 RootedValue v(cx);
173 str = input;
174 CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v));
175 CHECK_SAME(v, expected);
176 return true;
177 }
178 END_TEST(testParseJSON_success)
179
BEGIN_TEST(testParseJSON_error)180 BEGIN_TEST(testParseJSON_error)
181 {
182 CHECK(Error(cx, "" , 1, 1));
183 CHECK(Error(cx, "\n" , 2, 1));
184 CHECK(Error(cx, "\r" , 2, 1));
185 CHECK(Error(cx, "\r\n" , 2, 1));
186
187 CHECK(Error(cx, "[" , 1, 2));
188 CHECK(Error(cx, "[,]" , 1, 2));
189 CHECK(Error(cx, "[1,]" , 1, 4));
190 CHECK(Error(cx, "{a:2}" , 1, 2));
191 CHECK(Error(cx, "{\"a\":2,}" , 1, 8));
192 CHECK(Error(cx, "]" , 1, 1));
193 CHECK(Error(cx, "\"" , 1, 2));
194 CHECK(Error(cx, "{]" , 1, 2));
195 CHECK(Error(cx, "[}" , 1, 2));
196 CHECK(Error(cx, "'wrongly-quoted string'" , 1, 1));
197
198 CHECK(Error(cx, "{\"a\":2 \n b:3}" , 2, 2));
199 CHECK(Error(cx, "\n[" , 2, 2));
200 CHECK(Error(cx, "\n[,]" , 2, 2));
201 CHECK(Error(cx, "\n[1,]" , 2, 4));
202 CHECK(Error(cx, "\n{a:2}" , 2, 2));
203 CHECK(Error(cx, "\n{\"a\":2,}" , 2, 8));
204 CHECK(Error(cx, "\n]" , 2, 1));
205 CHECK(Error(cx, "\"bad string\n\"" , 1, 12));
206 CHECK(Error(cx, "\r'wrongly-quoted string'" , 2, 1));
207 CHECK(Error(cx, "\n\"" , 2, 2));
208 CHECK(Error(cx, "\n{]" , 2, 2));
209 CHECK(Error(cx, "\n[}" , 2, 2));
210 CHECK(Error(cx, "{\"a\":[2,3],\n\"b\":,5,6}" , 2, 5));
211
212 CHECK(Error(cx, "{\"a\":2 \r b:3}" , 2, 2));
213 CHECK(Error(cx, "\r[" , 2, 2));
214 CHECK(Error(cx, "\r[,]" , 2, 2));
215 CHECK(Error(cx, "\r[1,]" , 2, 4));
216 CHECK(Error(cx, "\r{a:2}" , 2, 2));
217 CHECK(Error(cx, "\r{\"a\":2,}" , 2, 8));
218 CHECK(Error(cx, "\r]" , 2, 1));
219 CHECK(Error(cx, "\"bad string\r\"" , 1, 12));
220 CHECK(Error(cx, "\r'wrongly-quoted string'" , 2, 1));
221 CHECK(Error(cx, "\r\"" , 2, 2));
222 CHECK(Error(cx, "\r{]" , 2, 2));
223 CHECK(Error(cx, "\r[}" , 2, 2));
224 CHECK(Error(cx, "{\"a\":[2,3],\r\"b\":,5,6}" , 2, 5));
225
226 CHECK(Error(cx, "{\"a\":2 \r\n b:3}" , 2, 2));
227 CHECK(Error(cx, "\r\n[" , 2, 2));
228 CHECK(Error(cx, "\r\n[,]" , 2, 2));
229 CHECK(Error(cx, "\r\n[1,]" , 2, 4));
230 CHECK(Error(cx, "\r\n{a:2}" , 2, 2));
231 CHECK(Error(cx, "\r\n{\"a\":2,}" , 2, 8));
232 CHECK(Error(cx, "\r\n]" , 2, 1));
233 CHECK(Error(cx, "\"bad string\r\n\"" , 1, 12));
234 CHECK(Error(cx, "\r\n'wrongly-quoted string'" , 2, 1));
235 CHECK(Error(cx, "\r\n\"" , 2, 2));
236 CHECK(Error(cx, "\r\n{]" , 2, 2));
237 CHECK(Error(cx, "\r\n[}" , 2, 2));
238 CHECK(Error(cx, "{\"a\":[2,3],\r\n\"b\":,5,6}" , 2, 5));
239
240 CHECK(Error(cx, "\n\"bad string\n\"" , 2, 12));
241 CHECK(Error(cx, "\r\"bad string\r\"" , 2, 12));
242 CHECK(Error(cx, "\r\n\"bad string\r\n\"" , 2, 12));
243
244 CHECK(Error(cx, "{\n\"a\":[2,3],\r\"b\":,5,6}" , 3, 5));
245 CHECK(Error(cx, "{\r\"a\":[2,3],\n\"b\":,5,6}" , 3, 5));
246 CHECK(Error(cx, "[\"\\t\\q" , 1, 6));
247 CHECK(Error(cx, "[\"\\t\x00" , 1, 5));
248 CHECK(Error(cx, "[\"\\t\x01" , 1, 5));
249 CHECK(Error(cx, "[\"\\t\\\x00" , 1, 6));
250 CHECK(Error(cx, "[\"\\t\\\x01" , 1, 6));
251
252 // Unicode escape errors are messy. The first bad character could be
253 // non-hexadecimal, or it could be absent entirely. Include tests where
254 // there's a bad character, followed by zero to as many characters as are
255 // needed to form a complete Unicode escape sequence, plus one. (The extra
256 // characters beyond are valuable because our implementation checks for
257 // too-few subsequent characters first, before checking for subsequent
258 // non-hexadecimal characters. So \u<END>, \u0<END>, \u00<END>, and
259 // \u000<END> are all *detected* as invalid by the same code path, but the
260 // process of computing the first invalid character follows a different
261 // code path for each. And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected
262 // as invalid by the same code path [ignoring which precise subexpression
263 // triggers failure of a single condition], but the computation of the
264 // first invalid character follows a different code path for each.)
265 CHECK(Error(cx, "[\"\\t\\u" , 1, 7));
266 CHECK(Error(cx, "[\"\\t\\uZ" , 1, 7));
267 CHECK(Error(cx, "[\"\\t\\uZZ" , 1, 7));
268 CHECK(Error(cx, "[\"\\t\\uZZZ" , 1, 7));
269 CHECK(Error(cx, "[\"\\t\\uZZZZ" , 1, 7));
270 CHECK(Error(cx, "[\"\\t\\uZZZZZ" , 1, 7));
271
272 CHECK(Error(cx, "[\"\\t\\u0" , 1, 8));
273 CHECK(Error(cx, "[\"\\t\\u0Z" , 1, 8));
274 CHECK(Error(cx, "[\"\\t\\u0ZZ" , 1, 8));
275 CHECK(Error(cx, "[\"\\t\\u0ZZZ" , 1, 8));
276 CHECK(Error(cx, "[\"\\t\\u0ZZZZ" , 1, 8));
277
278 CHECK(Error(cx, "[\"\\t\\u00" , 1, 9));
279 CHECK(Error(cx, "[\"\\t\\u00Z" , 1, 9));
280 CHECK(Error(cx, "[\"\\t\\u00ZZ" , 1, 9));
281 CHECK(Error(cx, "[\"\\t\\u00ZZZ" , 1, 9));
282
283 CHECK(Error(cx, "[\"\\t\\u000" , 1, 10));
284 CHECK(Error(cx, "[\"\\t\\u000Z" , 1, 10));
285 CHECK(Error(cx, "[\"\\t\\u000ZZ" , 1, 10));
286
287 return true;
288 }
289
290 template<size_t N> inline bool
Error(JSContext * cx,const char (& input)[N],uint32_t expectedLine,uint32_t expectedColumn)291 Error(JSContext* cx, const char (&input)[N], uint32_t expectedLine,
292 uint32_t expectedColumn)
293 {
294 AutoInflatedString str(cx);
295 RootedValue dummy(cx);
296 str = input;
297
298 bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
299 CHECK(!ok);
300
301 RootedValue exn(cx);
302 CHECK(JS_GetPendingException(cx, &exn));
303 JS_ClearPendingException(cx);
304
305 js::ErrorReport report(cx);
306 CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
307 CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE);
308
309 const char* lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn);
310 CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII) != nullptr);
311 js_free((void*)lineAndColumnASCII);
312
313 /* We do not execute JS, so there should be no exception thrown. */
314 CHECK(!JS_IsExceptionPending(cx));
315
316 return true;
317 }
END_TEST(testParseJSON_error)318 END_TEST(testParseJSON_error)
319
320 static bool
321 Censor(JSContext* cx, unsigned argc, JS::Value* vp)
322 {
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();
327 return true;
328 }
329
BEGIN_TEST(testParseJSON_reviver)330 BEGIN_TEST(testParseJSON_reviver)
331 {
332 JSFunction* fun = JS_NewFunction(cx, Censor, 0, 0, "censor");
333 CHECK(fun);
334
335 JS::RootedValue filter(cx, JS::ObjectValue(*JS_GetFunctionObject(fun)));
336
337 CHECK(TryParse(cx, "true", filter));
338 CHECK(TryParse(cx, "false", filter));
339 CHECK(TryParse(cx, "null", filter));
340 CHECK(TryParse(cx, "1", filter));
341 CHECK(TryParse(cx, "1.75", filter));
342 CHECK(TryParse(cx, "[]", filter));
343 CHECK(TryParse(cx, "[1]", filter));
344 CHECK(TryParse(cx, "{}", filter));
345 return true;
346 }
347
348 template<size_t N> inline bool
TryParse(JSContext * cx,const char (& input)[N],JS::HandleValue filter)349 TryParse(JSContext* cx, const char (&input)[N], JS::HandleValue filter)
350 {
351 AutoInflatedString str(cx);
352 JS::RootedValue v(cx);
353 str = input;
354 CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v));
355 CHECK(v.isNull());
356 return true;
357 }
358 END_TEST(testParseJSON_reviver)
359