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