testParseJSON.cpp (11033B)
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 "js/PropertyAndElement.h" // JS_GetProperty 18 #include "jsapi-tests/tests.h" 19 20 using namespace js; 21 22 class AutoInflatedString { 23 JSContext* const cx; 24 char16_t* chars_; 25 size_t length_; 26 27 public: 28 explicit AutoInflatedString(JSContext* cx) 29 : cx(cx), chars_(nullptr), length_(0) {} 30 ~AutoInflatedString() { JS_free(cx, chars_); } 31 32 template <size_t N> 33 void operator=(const char (&str)[N]) { 34 length_ = N - 1; 35 chars_ = InflateString(cx, str, length_); 36 if (!chars_) { 37 abort(); 38 } 39 } 40 41 void operator=(const char* str) { 42 length_ = strlen(str); 43 chars_ = InflateString(cx, str, length_); 44 if (!chars_) { 45 abort(); 46 } 47 } 48 49 const char16_t* chars() const { return chars_; } 50 size_t length() const { return length_; } 51 }; 52 53 BEGIN_TEST(testParseJSON_success) { 54 // Primitives 55 JS::RootedValue expected(cx); 56 expected = JS::TrueValue(); 57 CHECK(TryParse(cx, "true", expected)); 58 59 expected = JS::FalseValue(); 60 CHECK(TryParse(cx, "false", expected)); 61 62 expected = JS::NullValue(); 63 CHECK(TryParse(cx, "null", expected)); 64 65 expected.setInt32(0); 66 CHECK(TryParse(cx, "0", expected)); 67 68 expected.setInt32(1); 69 CHECK(TryParse(cx, "1", expected)); 70 71 expected.setInt32(-1); 72 CHECK(TryParse(cx, "-1", expected)); 73 74 expected.setDouble(1); 75 CHECK(TryParse(cx, "1", expected)); 76 77 expected.setDouble(1.75); 78 CHECK(TryParse(cx, "1.75", expected)); 79 80 expected.setDouble(9e9); 81 CHECK(TryParse(cx, "9e9", expected)); 82 83 expected.setDouble(std::numeric_limits<double>::infinity()); 84 CHECK(TryParse(cx, "9e99999", expected)); 85 86 JS::Rooted<JSLinearString*> str(cx); 87 88 const char16_t emptystr[] = {'\0'}; 89 str = js::NewStringCopyN<CanGC>(cx, emptystr, 0); 90 CHECK(str); 91 expected = JS::StringValue(str); 92 CHECK(TryParse(cx, "\"\"", expected)); 93 94 const char16_t nullstr[] = {'\0'}; 95 str = NewString(cx, nullstr); 96 CHECK(str); 97 expected = JS::StringValue(str); 98 CHECK(TryParse(cx, "\"\\u0000\"", expected)); 99 100 const char16_t backstr[] = {'\b'}; 101 str = NewString(cx, backstr); 102 CHECK(str); 103 expected = JS::StringValue(str); 104 CHECK(TryParse(cx, "\"\\b\"", expected)); 105 CHECK(TryParse(cx, "\"\\u0008\"", expected)); 106 107 const char16_t newlinestr[] = { 108 '\n', 109 }; 110 str = NewString(cx, newlinestr); 111 CHECK(str); 112 expected = JS::StringValue(str); 113 CHECK(TryParse(cx, "\"\\n\"", expected)); 114 CHECK(TryParse(cx, "\"\\u000A\"", expected)); 115 116 // Arrays 117 JS::RootedValue v(cx), v2(cx); 118 JS::RootedObject obj(cx); 119 120 bool isArray; 121 122 CHECK(Parse(cx, "[]", &v)); 123 CHECK(v.isObject()); 124 obj = &v.toObject(); 125 CHECK(JS::IsArrayObject(cx, obj, &isArray)); 126 CHECK(isArray); 127 CHECK(JS_GetProperty(cx, obj, "length", &v2)); 128 CHECK(v2.isInt32(0)); 129 130 CHECK(Parse(cx, "[1]", &v)); 131 CHECK(v.isObject()); 132 obj = &v.toObject(); 133 CHECK(JS::IsArrayObject(cx, obj, &isArray)); 134 CHECK(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)); 139 140 // Objects 141 CHECK(Parse(cx, "{}", &v)); 142 CHECK(v.isObject()); 143 obj = &v.toObject(); 144 CHECK(JS::IsArrayObject(cx, obj, &isArray)); 145 CHECK(!isArray); 146 147 CHECK(Parse(cx, "{ \"f\": 17 }", &v)); 148 CHECK(v.isObject()); 149 obj = &v.toObject(); 150 CHECK(JS::IsArrayObject(cx, obj, &isArray)); 151 CHECK(!isArray); 152 CHECK(JS_GetProperty(cx, obj, "f", &v2)); 153 CHECK(v2.isInt32(17)); 154 155 return true; 156 } 157 158 template <size_t N> 159 static JSLinearString* NewString(JSContext* cx, const char16_t (&chars)[N]) { 160 return js::NewStringCopyN<CanGC>(cx, chars, N); 161 } 162 163 template <size_t N> 164 inline bool Parse(JSContext* cx, const char (&input)[N], 165 JS::MutableHandleValue vp) { 166 AutoInflatedString str(cx); 167 str = input; 168 CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp)); 169 return true; 170 } 171 172 template <size_t N> 173 inline bool TryParse(JSContext* cx, const char (&input)[N], 174 JS::HandleValue expected) { 175 AutoInflatedString str(cx); 176 RootedValue v(cx); 177 str = input; 178 CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v)); 179 CHECK_SAME(v, expected); 180 return true; 181 } 182 END_TEST(testParseJSON_success) 183 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)); 189 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)); 200 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)); 214 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)); 228 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)); 242 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)); 246 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)); 254 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)); 274 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)); 280 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)); 285 286 CHECK(Error(cx, "[\"\\t\\u000", 1, 10)); 287 CHECK(Error(cx, "[\"\\t\\u000Z", 1, 10)); 288 CHECK(Error(cx, "[\"\\t\\u000ZZ", 1, 10)); 289 290 return true; 291 } 292 293 template <size_t N> 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); 298 str = input; 299 300 bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy); 301 CHECK(!ok); 302 303 JS::ExceptionStack exnStack(cx); 304 CHECK(StealPendingExceptionStack(cx, &exnStack)); 305 306 JS::ErrorReportBuilder report(cx); 307 CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); 308 CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE); 309 310 UniqueChars lineAndColumnASCII = 311 JS_smprintf("line %d column %d", expectedLine, expectedColumn); 312 CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII.get()) != 313 nullptr); 314 315 /* We do not execute JS, so there should be no exception thrown. */ 316 CHECK(!JS_IsExceptionPending(cx)); 317 318 return true; 319 } 320 END_TEST(testParseJSON_error) 321 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() == 3); 325 MOZ_RELEASE_ASSERT(args[0].isString()); 326 args.rval().setNull(); 327 return true; 328 } 329 330 BEGIN_TEST(testParseJSON_reviver) { 331 JSFunction* fun = JS_NewFunction(cx, Censor, 0, 0, "censor"); 332 CHECK(fun); 333 334 JS::RootedValue filter(cx, JS::ObjectValue(*JS_GetFunctionObject(fun))); 335 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)); 344 return true; 345 } 346 347 template <size_t N> 348 inline bool TryParse(JSContext* cx, const char (&input)[N], 349 JS::HandleValue filter) { 350 AutoInflatedString str(cx); 351 JS::RootedValue v(cx); 352 str = input; 353 CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v)); 354 CHECK(v.isNull()); 355 return true; 356 } 357 END_TEST(testParseJSON_reviver)