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