Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
8 #include <limits>
10 #include "jsstr.h"
12 #include "jsapi-tests/tests.h"
14 using namespace js;
16 class AutoInflatedString {
17 JSContext * const cx;
18 jschar *chars_;
19 size_t length_;
21 public:
22 AutoInflatedString(JSContext *cx) : cx(cx), chars_(nullptr), length_(0) { }
23 ~AutoInflatedString() {
24 JS_free(cx, chars_);
25 }
27 template<size_t N> void operator=(const char (&str)[N]) {
28 length_ = N - 1;
29 chars_ = InflateString(cx, str, &length_);
30 if (!chars_)
31 abort();
32 }
34 const jschar *chars() const { return chars_; }
35 size_t length() const { return length_; }
36 };
38 BEGIN_TEST(testParseJSON_success)
39 {
40 // Primitives
41 JS::RootedValue expected(cx);
42 expected = JSVAL_TRUE;
43 CHECK(TryParse(cx, "true", expected));
45 expected = JSVAL_FALSE;
46 CHECK(TryParse(cx, "false", expected));
48 expected = JSVAL_NULL;
49 CHECK(TryParse(cx, "null", expected));
51 expected = INT_TO_JSVAL(0);
52 CHECK(TryParse(cx, "0", expected));
54 expected = INT_TO_JSVAL(1);
55 CHECK(TryParse(cx, "1", expected));
57 expected = INT_TO_JSVAL(-1);
58 CHECK(TryParse(cx, "-1", expected));
60 expected = DOUBLE_TO_JSVAL(1);
61 CHECK(TryParse(cx, "1", expected));
63 expected = DOUBLE_TO_JSVAL(1.75);
64 CHECK(TryParse(cx, "1.75", expected));
66 expected = DOUBLE_TO_JSVAL(9e9);
67 CHECK(TryParse(cx, "9e9", expected));
69 expected = DOUBLE_TO_JSVAL(std::numeric_limits<double>::infinity());
70 CHECK(TryParse(cx, "9e99999", expected));
72 JS::Rooted<JSFlatString*> str(cx);
74 const jschar emptystr[] = { '\0' };
75 str = js_NewStringCopyN<CanGC>(cx, emptystr, 0);
76 CHECK(str);
77 expected = STRING_TO_JSVAL(str);
78 CHECK(TryParse(cx, "\"\"", expected));
80 const jschar nullstr[] = { '\0' };
81 str = NewString(cx, nullstr);
82 CHECK(str);
83 expected = STRING_TO_JSVAL(str);
84 CHECK(TryParse(cx, "\"\\u0000\"", expected));
86 const jschar backstr[] = { '\b' };
87 str = NewString(cx, backstr);
88 CHECK(str);
89 expected = STRING_TO_JSVAL(str);
90 CHECK(TryParse(cx, "\"\\b\"", expected));
91 CHECK(TryParse(cx, "\"\\u0008\"", expected));
93 const jschar newlinestr[] = { '\n', };
94 str = NewString(cx, newlinestr);
95 CHECK(str);
96 expected = STRING_TO_JSVAL(str);
97 CHECK(TryParse(cx, "\"\\n\"", expected));
98 CHECK(TryParse(cx, "\"\\u000A\"", expected));
101 // Arrays
102 JS::RootedValue v(cx), v2(cx);
103 JS::RootedObject obj(cx);
105 CHECK(Parse(cx, "[]", &v));
106 CHECK(!JSVAL_IS_PRIMITIVE(v));
107 obj = JSVAL_TO_OBJECT(v);
108 CHECK(JS_IsArrayObject(cx, obj));
109 CHECK(JS_GetProperty(cx, obj, "length", &v2));
110 CHECK_SAME(v2, JSVAL_ZERO);
112 CHECK(Parse(cx, "[1]", &v));
113 CHECK(!JSVAL_IS_PRIMITIVE(v));
114 obj = JSVAL_TO_OBJECT(v);
115 CHECK(JS_IsArrayObject(cx, obj));
116 CHECK(JS_GetProperty(cx, obj, "0", &v2));
117 CHECK_SAME(v2, JSVAL_ONE);
118 CHECK(JS_GetProperty(cx, obj, "length", &v2));
119 CHECK_SAME(v2, JSVAL_ONE);
122 // Objects
123 CHECK(Parse(cx, "{}", &v));
124 CHECK(!JSVAL_IS_PRIMITIVE(v));
125 obj = JSVAL_TO_OBJECT(v);
126 CHECK(!JS_IsArrayObject(cx, obj));
128 CHECK(Parse(cx, "{ \"f\": 17 }", &v));
129 CHECK(!JSVAL_IS_PRIMITIVE(v));
130 obj = JSVAL_TO_OBJECT(v);
131 CHECK(!JS_IsArrayObject(cx, obj));
132 CHECK(JS_GetProperty(cx, obj, "f", &v2));
133 CHECK_SAME(v2, INT_TO_JSVAL(17));
135 return true;
136 }
138 template<size_t N> static JSFlatString *
139 NewString(JSContext *cx, const jschar (&chars)[N])
140 {
141 return js_NewStringCopyN<CanGC>(cx, chars, N);
142 }
144 template<size_t N> inline bool
145 Parse(JSContext *cx, const char (&input)[N], JS::MutableHandleValue vp)
146 {
147 AutoInflatedString str(cx);
148 str = input;
149 CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp));
150 return true;
151 }
153 template<size_t N> inline bool
154 TryParse(JSContext *cx, const char (&input)[N], JS::HandleValue expected)
155 {
156 AutoInflatedString str(cx);
157 RootedValue v(cx);
158 str = input;
159 CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v));
160 CHECK_SAME(v, expected);
161 return true;
162 }
163 END_TEST(testParseJSON_success)
165 BEGIN_TEST(testParseJSON_error)
166 {
167 CHECK(Error(cx, "" , "1", "1"));
168 CHECK(Error(cx, "\n" , "2", "1"));
169 CHECK(Error(cx, "\r" , "2", "1"));
170 CHECK(Error(cx, "\r\n" , "2", "1"));
172 CHECK(Error(cx, "[" , "1", "2"));
173 CHECK(Error(cx, "[,]" , "1", "2"));
174 CHECK(Error(cx, "[1,]" , "1", "4"));
175 CHECK(Error(cx, "{a:2}" , "1", "2"));
176 CHECK(Error(cx, "{\"a\":2,}" , "1", "8"));
177 CHECK(Error(cx, "]" , "1", "1"));
178 CHECK(Error(cx, "\"" , "1", "2"));
179 CHECK(Error(cx, "{]" , "1", "2"));
180 CHECK(Error(cx, "[}" , "1", "2"));
181 CHECK(Error(cx, "'wrongly-quoted string'" , "1", "1"));
183 CHECK(Error(cx, "{\"a\":2 \n b:3}" , "2", "2"));
184 CHECK(Error(cx, "\n[" , "2", "2"));
185 CHECK(Error(cx, "\n[,]" , "2", "2"));
186 CHECK(Error(cx, "\n[1,]" , "2", "4"));
187 CHECK(Error(cx, "\n{a:2}" , "2", "2"));
188 CHECK(Error(cx, "\n{\"a\":2,}" , "2", "8"));
189 CHECK(Error(cx, "\n]" , "2", "1"));
190 CHECK(Error(cx, "\"bad string\n\"" , "1", "12"));
191 CHECK(Error(cx, "\r'wrongly-quoted string'" , "2", "1"));
192 CHECK(Error(cx, "\n\"" , "2", "2"));
193 CHECK(Error(cx, "\n{]" , "2", "2"));
194 CHECK(Error(cx, "\n[}" , "2", "2"));
195 CHECK(Error(cx, "{\"a\":[2,3],\n\"b\":,5,6}" , "2", "5"));
197 CHECK(Error(cx, "{\"a\":2 \r b:3}" , "2", "2"));
198 CHECK(Error(cx, "\r[" , "2", "2"));
199 CHECK(Error(cx, "\r[,]" , "2", "2"));
200 CHECK(Error(cx, "\r[1,]" , "2", "4"));
201 CHECK(Error(cx, "\r{a:2}" , "2", "2"));
202 CHECK(Error(cx, "\r{\"a\":2,}" , "2", "8"));
203 CHECK(Error(cx, "\r]" , "2", "1"));
204 CHECK(Error(cx, "\"bad string\r\"" , "1", "12"));
205 CHECK(Error(cx, "\r'wrongly-quoted string'" , "2", "1"));
206 CHECK(Error(cx, "\r\"" , "2", "2"));
207 CHECK(Error(cx, "\r{]" , "2", "2"));
208 CHECK(Error(cx, "\r[}" , "2", "2"));
209 CHECK(Error(cx, "{\"a\":[2,3],\r\"b\":,5,6}" , "2", "5"));
211 CHECK(Error(cx, "{\"a\":2 \r\n b:3}" , "2", "2"));
212 CHECK(Error(cx, "\r\n[" , "2", "2"));
213 CHECK(Error(cx, "\r\n[,]" , "2", "2"));
214 CHECK(Error(cx, "\r\n[1,]" , "2", "4"));
215 CHECK(Error(cx, "\r\n{a:2}" , "2", "2"));
216 CHECK(Error(cx, "\r\n{\"a\":2,}" , "2", "8"));
217 CHECK(Error(cx, "\r\n]" , "2", "1"));
218 CHECK(Error(cx, "\"bad string\r\n\"" , "1", "12"));
219 CHECK(Error(cx, "\r\n'wrongly-quoted string'" , "2", "1"));
220 CHECK(Error(cx, "\r\n\"" , "2", "2"));
221 CHECK(Error(cx, "\r\n{]" , "2", "2"));
222 CHECK(Error(cx, "\r\n[}" , "2", "2"));
223 CHECK(Error(cx, "{\"a\":[2,3],\r\n\"b\":,5,6}" , "2", "5"));
225 CHECK(Error(cx, "\n\"bad string\n\"" , "2", "12"));
226 CHECK(Error(cx, "\r\"bad string\r\"" , "2", "12"));
227 CHECK(Error(cx, "\r\n\"bad string\r\n\"" , "2", "12"));
229 CHECK(Error(cx, "{\n\"a\":[2,3],\r\"b\":,5,6}" , "3", "5"));
230 CHECK(Error(cx, "{\r\"a\":[2,3],\n\"b\":,5,6}" , "3", "5"));
231 CHECK(Error(cx, "[\"\\t\\q" , "1", "6"));
232 CHECK(Error(cx, "[\"\\t\x00" , "1", "5"));
233 CHECK(Error(cx, "[\"\\t\x01" , "1", "5"));
234 CHECK(Error(cx, "[\"\\t\\\x00" , "1", "6"));
235 CHECK(Error(cx, "[\"\\t\\\x01" , "1", "6"));
237 // Unicode escape errors are messy. The first bad character could be
238 // non-hexadecimal, or it could be absent entirely. Include tests where
239 // there's a bad character, followed by zero to as many characters as are
240 // needed to form a complete Unicode escape sequence, plus one. (The extra
241 // characters beyond are valuable because our implementation checks for
242 // too-few subsequent characters first, before checking for subsequent
243 // non-hexadecimal characters. So \u<END>, \u0<END>, \u00<END>, and
244 // \u000<END> are all *detected* as invalid by the same code path, but the
245 // process of computing the first invalid character follows a different
246 // code path for each. And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected
247 // as invalid by the same code path [ignoring which precise subexpression
248 // triggers failure of a single condition], but the computation of the
249 // first invalid character follows a different code path for each.)
250 CHECK(Error(cx, "[\"\\t\\u" , "1", "7"));
251 CHECK(Error(cx, "[\"\\t\\uZ" , "1", "7"));
252 CHECK(Error(cx, "[\"\\t\\uZZ" , "1", "7"));
253 CHECK(Error(cx, "[\"\\t\\uZZZ" , "1", "7"));
254 CHECK(Error(cx, "[\"\\t\\uZZZZ" , "1", "7"));
255 CHECK(Error(cx, "[\"\\t\\uZZZZZ" , "1", "7"));
257 CHECK(Error(cx, "[\"\\t\\u0" , "1", "8"));
258 CHECK(Error(cx, "[\"\\t\\u0Z" , "1", "8"));
259 CHECK(Error(cx, "[\"\\t\\u0ZZ" , "1", "8"));
260 CHECK(Error(cx, "[\"\\t\\u0ZZZ" , "1", "8"));
261 CHECK(Error(cx, "[\"\\t\\u0ZZZZ" , "1", "8"));
263 CHECK(Error(cx, "[\"\\t\\u00" , "1", "9"));
264 CHECK(Error(cx, "[\"\\t\\u00Z" , "1", "9"));
265 CHECK(Error(cx, "[\"\\t\\u00ZZ" , "1", "9"));
266 CHECK(Error(cx, "[\"\\t\\u00ZZZ" , "1", "9"));
268 CHECK(Error(cx, "[\"\\t\\u000" , "1", "10"));
269 CHECK(Error(cx, "[\"\\t\\u000Z" , "1", "10"));
270 CHECK(Error(cx, "[\"\\t\\u000ZZ" , "1", "10"));
272 return true;
273 }
275 template<size_t N, size_t M, size_t L> inline bool
276 Error(JSContext *cx, const char (&input)[N], const char (&expectedLine)[M],
277 const char (&expectedColumn)[L])
278 {
279 AutoInflatedString str(cx), line(cx), column(cx);
280 RootedValue dummy(cx);
281 str = input;
283 ContextPrivate p = {0, 0};
284 CHECK(!JS_GetContextPrivate(cx));
285 JS_SetContextPrivate(cx, &p);
286 JSErrorReporter old = JS_SetErrorReporter(cx, ReportJSONError);
287 bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
288 JS_SetErrorReporter(cx, old);
289 JS_SetContextPrivate(cx, nullptr);
291 CHECK(!ok);
292 CHECK(!p.unexpectedErrorCount);
293 CHECK(p.expectedErrorCount == 1);
294 column = expectedColumn;
295 CHECK(js_strcmp(column.chars(), p.column) == 0);
296 line = expectedLine;
297 CHECK(js_strcmp(line.chars(), p.line) == 0);
299 /* We do not execute JS, so there should be no exception thrown. */
300 CHECK(!JS_IsExceptionPending(cx));
302 return true;
303 }
305 struct ContextPrivate {
306 static const size_t MaxSize = sizeof("4294967295");
307 unsigned unexpectedErrorCount;
308 unsigned expectedErrorCount;
309 jschar column[MaxSize];
310 jschar line[MaxSize];
311 };
313 static void
314 ReportJSONError(JSContext *cx, const char *message, JSErrorReport *report)
315 {
316 ContextPrivate *p = static_cast<ContextPrivate *>(JS_GetContextPrivate(cx));
317 // Although messageArgs[1] and messageArgs[2] are jschar*, we cast them to char*
318 // here because JSONParser::error() stores char* strings in them.
319 js_strncpy(p->line, report->messageArgs[1], js_strlen(report->messageArgs[1]));
320 js_strncpy(p->column, report->messageArgs[2], js_strlen(report->messageArgs[2]));
321 if (report->errorNumber == JSMSG_JSON_BAD_PARSE)
322 p->expectedErrorCount++;
323 else
324 p->unexpectedErrorCount++;
325 }
327 END_TEST(testParseJSON_error)
329 static bool
330 Censor(JSContext *cx, unsigned argc, jsval *vp)
331 {
332 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
333 JS_ASSERT(args.length() == 2);
334 #ifdef DEBUG
335 JS_ASSERT(args[0].isString());
336 #endif
337 args.rval().setNull();
338 return true;
339 }
341 BEGIN_TEST(testParseJSON_reviver)
342 {
343 JSFunction *fun = JS_NewFunction(cx, Censor, 0, 0, global, "censor");
344 CHECK(fun);
346 JS::RootedValue filter(cx, OBJECT_TO_JSVAL(JS_GetFunctionObject(fun)));
348 CHECK(TryParse(cx, "true", filter));
349 CHECK(TryParse(cx, "false", filter));
350 CHECK(TryParse(cx, "null", filter));
351 CHECK(TryParse(cx, "1", filter));
352 CHECK(TryParse(cx, "1.75", filter));
353 CHECK(TryParse(cx, "[]", filter));
354 CHECK(TryParse(cx, "[1]", filter));
355 CHECK(TryParse(cx, "{}", filter));
356 return true;
357 }
359 template<size_t N> inline bool
360 TryParse(JSContext *cx, const char (&input)[N], JS::HandleValue filter)
361 {
362 AutoInflatedString str(cx);
363 JS::RootedValue v(cx);
364 str = input;
365 CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v));
366 CHECK_SAME(v, JSVAL_NULL);
367 return true;
368 }
369 END_TEST(testParseJSON_reviver)