1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsapi-tests/testParseJSON.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,369 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include <limits> 1.12 + 1.13 +#include "jsstr.h" 1.14 + 1.15 +#include "jsapi-tests/tests.h" 1.16 + 1.17 +using namespace js; 1.18 + 1.19 +class AutoInflatedString { 1.20 + JSContext * const cx; 1.21 + jschar *chars_; 1.22 + size_t length_; 1.23 + 1.24 + public: 1.25 + AutoInflatedString(JSContext *cx) : cx(cx), chars_(nullptr), length_(0) { } 1.26 + ~AutoInflatedString() { 1.27 + JS_free(cx, chars_); 1.28 + } 1.29 + 1.30 + template<size_t N> void operator=(const char (&str)[N]) { 1.31 + length_ = N - 1; 1.32 + chars_ = InflateString(cx, str, &length_); 1.33 + if (!chars_) 1.34 + abort(); 1.35 + } 1.36 + 1.37 + const jschar *chars() const { return chars_; } 1.38 + size_t length() const { return length_; } 1.39 +}; 1.40 + 1.41 +BEGIN_TEST(testParseJSON_success) 1.42 +{ 1.43 + // Primitives 1.44 + JS::RootedValue expected(cx); 1.45 + expected = JSVAL_TRUE; 1.46 + CHECK(TryParse(cx, "true", expected)); 1.47 + 1.48 + expected = JSVAL_FALSE; 1.49 + CHECK(TryParse(cx, "false", expected)); 1.50 + 1.51 + expected = JSVAL_NULL; 1.52 + CHECK(TryParse(cx, "null", expected)); 1.53 + 1.54 + expected = INT_TO_JSVAL(0); 1.55 + CHECK(TryParse(cx, "0", expected)); 1.56 + 1.57 + expected = INT_TO_JSVAL(1); 1.58 + CHECK(TryParse(cx, "1", expected)); 1.59 + 1.60 + expected = INT_TO_JSVAL(-1); 1.61 + CHECK(TryParse(cx, "-1", expected)); 1.62 + 1.63 + expected = DOUBLE_TO_JSVAL(1); 1.64 + CHECK(TryParse(cx, "1", expected)); 1.65 + 1.66 + expected = DOUBLE_TO_JSVAL(1.75); 1.67 + CHECK(TryParse(cx, "1.75", expected)); 1.68 + 1.69 + expected = DOUBLE_TO_JSVAL(9e9); 1.70 + CHECK(TryParse(cx, "9e9", expected)); 1.71 + 1.72 + expected = DOUBLE_TO_JSVAL(std::numeric_limits<double>::infinity()); 1.73 + CHECK(TryParse(cx, "9e99999", expected)); 1.74 + 1.75 + JS::Rooted<JSFlatString*> str(cx); 1.76 + 1.77 + const jschar emptystr[] = { '\0' }; 1.78 + str = js_NewStringCopyN<CanGC>(cx, emptystr, 0); 1.79 + CHECK(str); 1.80 + expected = STRING_TO_JSVAL(str); 1.81 + CHECK(TryParse(cx, "\"\"", expected)); 1.82 + 1.83 + const jschar nullstr[] = { '\0' }; 1.84 + str = NewString(cx, nullstr); 1.85 + CHECK(str); 1.86 + expected = STRING_TO_JSVAL(str); 1.87 + CHECK(TryParse(cx, "\"\\u0000\"", expected)); 1.88 + 1.89 + const jschar backstr[] = { '\b' }; 1.90 + str = NewString(cx, backstr); 1.91 + CHECK(str); 1.92 + expected = STRING_TO_JSVAL(str); 1.93 + CHECK(TryParse(cx, "\"\\b\"", expected)); 1.94 + CHECK(TryParse(cx, "\"\\u0008\"", expected)); 1.95 + 1.96 + const jschar newlinestr[] = { '\n', }; 1.97 + str = NewString(cx, newlinestr); 1.98 + CHECK(str); 1.99 + expected = STRING_TO_JSVAL(str); 1.100 + CHECK(TryParse(cx, "\"\\n\"", expected)); 1.101 + CHECK(TryParse(cx, "\"\\u000A\"", expected)); 1.102 + 1.103 + 1.104 + // Arrays 1.105 + JS::RootedValue v(cx), v2(cx); 1.106 + JS::RootedObject obj(cx); 1.107 + 1.108 + CHECK(Parse(cx, "[]", &v)); 1.109 + CHECK(!JSVAL_IS_PRIMITIVE(v)); 1.110 + obj = JSVAL_TO_OBJECT(v); 1.111 + CHECK(JS_IsArrayObject(cx, obj)); 1.112 + CHECK(JS_GetProperty(cx, obj, "length", &v2)); 1.113 + CHECK_SAME(v2, JSVAL_ZERO); 1.114 + 1.115 + CHECK(Parse(cx, "[1]", &v)); 1.116 + CHECK(!JSVAL_IS_PRIMITIVE(v)); 1.117 + obj = JSVAL_TO_OBJECT(v); 1.118 + CHECK(JS_IsArrayObject(cx, obj)); 1.119 + CHECK(JS_GetProperty(cx, obj, "0", &v2)); 1.120 + CHECK_SAME(v2, JSVAL_ONE); 1.121 + CHECK(JS_GetProperty(cx, obj, "length", &v2)); 1.122 + CHECK_SAME(v2, JSVAL_ONE); 1.123 + 1.124 + 1.125 + // Objects 1.126 + CHECK(Parse(cx, "{}", &v)); 1.127 + CHECK(!JSVAL_IS_PRIMITIVE(v)); 1.128 + obj = JSVAL_TO_OBJECT(v); 1.129 + CHECK(!JS_IsArrayObject(cx, obj)); 1.130 + 1.131 + CHECK(Parse(cx, "{ \"f\": 17 }", &v)); 1.132 + CHECK(!JSVAL_IS_PRIMITIVE(v)); 1.133 + obj = JSVAL_TO_OBJECT(v); 1.134 + CHECK(!JS_IsArrayObject(cx, obj)); 1.135 + CHECK(JS_GetProperty(cx, obj, "f", &v2)); 1.136 + CHECK_SAME(v2, INT_TO_JSVAL(17)); 1.137 + 1.138 + return true; 1.139 +} 1.140 + 1.141 +template<size_t N> static JSFlatString * 1.142 +NewString(JSContext *cx, const jschar (&chars)[N]) 1.143 +{ 1.144 + return js_NewStringCopyN<CanGC>(cx, chars, N); 1.145 +} 1.146 + 1.147 +template<size_t N> inline bool 1.148 +Parse(JSContext *cx, const char (&input)[N], JS::MutableHandleValue vp) 1.149 +{ 1.150 + AutoInflatedString str(cx); 1.151 + str = input; 1.152 + CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp)); 1.153 + return true; 1.154 +} 1.155 + 1.156 +template<size_t N> inline bool 1.157 +TryParse(JSContext *cx, const char (&input)[N], JS::HandleValue expected) 1.158 +{ 1.159 + AutoInflatedString str(cx); 1.160 + RootedValue v(cx); 1.161 + str = input; 1.162 + CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v)); 1.163 + CHECK_SAME(v, expected); 1.164 + return true; 1.165 +} 1.166 +END_TEST(testParseJSON_success) 1.167 + 1.168 +BEGIN_TEST(testParseJSON_error) 1.169 +{ 1.170 + CHECK(Error(cx, "" , "1", "1")); 1.171 + CHECK(Error(cx, "\n" , "2", "1")); 1.172 + CHECK(Error(cx, "\r" , "2", "1")); 1.173 + CHECK(Error(cx, "\r\n" , "2", "1")); 1.174 + 1.175 + CHECK(Error(cx, "[" , "1", "2")); 1.176 + CHECK(Error(cx, "[,]" , "1", "2")); 1.177 + CHECK(Error(cx, "[1,]" , "1", "4")); 1.178 + CHECK(Error(cx, "{a:2}" , "1", "2")); 1.179 + CHECK(Error(cx, "{\"a\":2,}" , "1", "8")); 1.180 + CHECK(Error(cx, "]" , "1", "1")); 1.181 + CHECK(Error(cx, "\"" , "1", "2")); 1.182 + CHECK(Error(cx, "{]" , "1", "2")); 1.183 + CHECK(Error(cx, "[}" , "1", "2")); 1.184 + CHECK(Error(cx, "'wrongly-quoted string'" , "1", "1")); 1.185 + 1.186 + CHECK(Error(cx, "{\"a\":2 \n b:3}" , "2", "2")); 1.187 + CHECK(Error(cx, "\n[" , "2", "2")); 1.188 + CHECK(Error(cx, "\n[,]" , "2", "2")); 1.189 + CHECK(Error(cx, "\n[1,]" , "2", "4")); 1.190 + CHECK(Error(cx, "\n{a:2}" , "2", "2")); 1.191 + CHECK(Error(cx, "\n{\"a\":2,}" , "2", "8")); 1.192 + CHECK(Error(cx, "\n]" , "2", "1")); 1.193 + CHECK(Error(cx, "\"bad string\n\"" , "1", "12")); 1.194 + CHECK(Error(cx, "\r'wrongly-quoted string'" , "2", "1")); 1.195 + CHECK(Error(cx, "\n\"" , "2", "2")); 1.196 + CHECK(Error(cx, "\n{]" , "2", "2")); 1.197 + CHECK(Error(cx, "\n[}" , "2", "2")); 1.198 + CHECK(Error(cx, "{\"a\":[2,3],\n\"b\":,5,6}" , "2", "5")); 1.199 + 1.200 + CHECK(Error(cx, "{\"a\":2 \r b:3}" , "2", "2")); 1.201 + CHECK(Error(cx, "\r[" , "2", "2")); 1.202 + CHECK(Error(cx, "\r[,]" , "2", "2")); 1.203 + CHECK(Error(cx, "\r[1,]" , "2", "4")); 1.204 + CHECK(Error(cx, "\r{a:2}" , "2", "2")); 1.205 + CHECK(Error(cx, "\r{\"a\":2,}" , "2", "8")); 1.206 + CHECK(Error(cx, "\r]" , "2", "1")); 1.207 + CHECK(Error(cx, "\"bad string\r\"" , "1", "12")); 1.208 + CHECK(Error(cx, "\r'wrongly-quoted string'" , "2", "1")); 1.209 + CHECK(Error(cx, "\r\"" , "2", "2")); 1.210 + CHECK(Error(cx, "\r{]" , "2", "2")); 1.211 + CHECK(Error(cx, "\r[}" , "2", "2")); 1.212 + CHECK(Error(cx, "{\"a\":[2,3],\r\"b\":,5,6}" , "2", "5")); 1.213 + 1.214 + CHECK(Error(cx, "{\"a\":2 \r\n b:3}" , "2", "2")); 1.215 + CHECK(Error(cx, "\r\n[" , "2", "2")); 1.216 + CHECK(Error(cx, "\r\n[,]" , "2", "2")); 1.217 + CHECK(Error(cx, "\r\n[1,]" , "2", "4")); 1.218 + CHECK(Error(cx, "\r\n{a:2}" , "2", "2")); 1.219 + CHECK(Error(cx, "\r\n{\"a\":2,}" , "2", "8")); 1.220 + CHECK(Error(cx, "\r\n]" , "2", "1")); 1.221 + CHECK(Error(cx, "\"bad string\r\n\"" , "1", "12")); 1.222 + CHECK(Error(cx, "\r\n'wrongly-quoted string'" , "2", "1")); 1.223 + CHECK(Error(cx, "\r\n\"" , "2", "2")); 1.224 + CHECK(Error(cx, "\r\n{]" , "2", "2")); 1.225 + CHECK(Error(cx, "\r\n[}" , "2", "2")); 1.226 + CHECK(Error(cx, "{\"a\":[2,3],\r\n\"b\":,5,6}" , "2", "5")); 1.227 + 1.228 + CHECK(Error(cx, "\n\"bad string\n\"" , "2", "12")); 1.229 + CHECK(Error(cx, "\r\"bad string\r\"" , "2", "12")); 1.230 + CHECK(Error(cx, "\r\n\"bad string\r\n\"" , "2", "12")); 1.231 + 1.232 + CHECK(Error(cx, "{\n\"a\":[2,3],\r\"b\":,5,6}" , "3", "5")); 1.233 + CHECK(Error(cx, "{\r\"a\":[2,3],\n\"b\":,5,6}" , "3", "5")); 1.234 + CHECK(Error(cx, "[\"\\t\\q" , "1", "6")); 1.235 + CHECK(Error(cx, "[\"\\t\x00" , "1", "5")); 1.236 + CHECK(Error(cx, "[\"\\t\x01" , "1", "5")); 1.237 + CHECK(Error(cx, "[\"\\t\\\x00" , "1", "6")); 1.238 + CHECK(Error(cx, "[\"\\t\\\x01" , "1", "6")); 1.239 + 1.240 + // Unicode escape errors are messy. The first bad character could be 1.241 + // non-hexadecimal, or it could be absent entirely. Include tests where 1.242 + // there's a bad character, followed by zero to as many characters as are 1.243 + // needed to form a complete Unicode escape sequence, plus one. (The extra 1.244 + // characters beyond are valuable because our implementation checks for 1.245 + // too-few subsequent characters first, before checking for subsequent 1.246 + // non-hexadecimal characters. So \u<END>, \u0<END>, \u00<END>, and 1.247 + // \u000<END> are all *detected* as invalid by the same code path, but the 1.248 + // process of computing the first invalid character follows a different 1.249 + // code path for each. And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected 1.250 + // as invalid by the same code path [ignoring which precise subexpression 1.251 + // triggers failure of a single condition], but the computation of the 1.252 + // first invalid character follows a different code path for each.) 1.253 + CHECK(Error(cx, "[\"\\t\\u" , "1", "7")); 1.254 + CHECK(Error(cx, "[\"\\t\\uZ" , "1", "7")); 1.255 + CHECK(Error(cx, "[\"\\t\\uZZ" , "1", "7")); 1.256 + CHECK(Error(cx, "[\"\\t\\uZZZ" , "1", "7")); 1.257 + CHECK(Error(cx, "[\"\\t\\uZZZZ" , "1", "7")); 1.258 + CHECK(Error(cx, "[\"\\t\\uZZZZZ" , "1", "7")); 1.259 + 1.260 + CHECK(Error(cx, "[\"\\t\\u0" , "1", "8")); 1.261 + CHECK(Error(cx, "[\"\\t\\u0Z" , "1", "8")); 1.262 + CHECK(Error(cx, "[\"\\t\\u0ZZ" , "1", "8")); 1.263 + CHECK(Error(cx, "[\"\\t\\u0ZZZ" , "1", "8")); 1.264 + CHECK(Error(cx, "[\"\\t\\u0ZZZZ" , "1", "8")); 1.265 + 1.266 + CHECK(Error(cx, "[\"\\t\\u00" , "1", "9")); 1.267 + CHECK(Error(cx, "[\"\\t\\u00Z" , "1", "9")); 1.268 + CHECK(Error(cx, "[\"\\t\\u00ZZ" , "1", "9")); 1.269 + CHECK(Error(cx, "[\"\\t\\u00ZZZ" , "1", "9")); 1.270 + 1.271 + CHECK(Error(cx, "[\"\\t\\u000" , "1", "10")); 1.272 + CHECK(Error(cx, "[\"\\t\\u000Z" , "1", "10")); 1.273 + CHECK(Error(cx, "[\"\\t\\u000ZZ" , "1", "10")); 1.274 + 1.275 + return true; 1.276 +} 1.277 + 1.278 +template<size_t N, size_t M, size_t L> inline bool 1.279 +Error(JSContext *cx, const char (&input)[N], const char (&expectedLine)[M], 1.280 + const char (&expectedColumn)[L]) 1.281 +{ 1.282 + AutoInflatedString str(cx), line(cx), column(cx); 1.283 + RootedValue dummy(cx); 1.284 + str = input; 1.285 + 1.286 + ContextPrivate p = {0, 0}; 1.287 + CHECK(!JS_GetContextPrivate(cx)); 1.288 + JS_SetContextPrivate(cx, &p); 1.289 + JSErrorReporter old = JS_SetErrorReporter(cx, ReportJSONError); 1.290 + bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy); 1.291 + JS_SetErrorReporter(cx, old); 1.292 + JS_SetContextPrivate(cx, nullptr); 1.293 + 1.294 + CHECK(!ok); 1.295 + CHECK(!p.unexpectedErrorCount); 1.296 + CHECK(p.expectedErrorCount == 1); 1.297 + column = expectedColumn; 1.298 + CHECK(js_strcmp(column.chars(), p.column) == 0); 1.299 + line = expectedLine; 1.300 + CHECK(js_strcmp(line.chars(), p.line) == 0); 1.301 + 1.302 + /* We do not execute JS, so there should be no exception thrown. */ 1.303 + CHECK(!JS_IsExceptionPending(cx)); 1.304 + 1.305 + return true; 1.306 +} 1.307 + 1.308 +struct ContextPrivate { 1.309 + static const size_t MaxSize = sizeof("4294967295"); 1.310 + unsigned unexpectedErrorCount; 1.311 + unsigned expectedErrorCount; 1.312 + jschar column[MaxSize]; 1.313 + jschar line[MaxSize]; 1.314 +}; 1.315 + 1.316 +static void 1.317 +ReportJSONError(JSContext *cx, const char *message, JSErrorReport *report) 1.318 +{ 1.319 + ContextPrivate *p = static_cast<ContextPrivate *>(JS_GetContextPrivate(cx)); 1.320 + // Although messageArgs[1] and messageArgs[2] are jschar*, we cast them to char* 1.321 + // here because JSONParser::error() stores char* strings in them. 1.322 + js_strncpy(p->line, report->messageArgs[1], js_strlen(report->messageArgs[1])); 1.323 + js_strncpy(p->column, report->messageArgs[2], js_strlen(report->messageArgs[2])); 1.324 + if (report->errorNumber == JSMSG_JSON_BAD_PARSE) 1.325 + p->expectedErrorCount++; 1.326 + else 1.327 + p->unexpectedErrorCount++; 1.328 +} 1.329 + 1.330 +END_TEST(testParseJSON_error) 1.331 + 1.332 +static bool 1.333 +Censor(JSContext *cx, unsigned argc, jsval *vp) 1.334 +{ 1.335 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.336 + JS_ASSERT(args.length() == 2); 1.337 +#ifdef DEBUG 1.338 + JS_ASSERT(args[0].isString()); 1.339 +#endif 1.340 + args.rval().setNull(); 1.341 + return true; 1.342 +} 1.343 + 1.344 +BEGIN_TEST(testParseJSON_reviver) 1.345 +{ 1.346 + JSFunction *fun = JS_NewFunction(cx, Censor, 0, 0, global, "censor"); 1.347 + CHECK(fun); 1.348 + 1.349 + JS::RootedValue filter(cx, OBJECT_TO_JSVAL(JS_GetFunctionObject(fun))); 1.350 + 1.351 + CHECK(TryParse(cx, "true", filter)); 1.352 + CHECK(TryParse(cx, "false", filter)); 1.353 + CHECK(TryParse(cx, "null", filter)); 1.354 + CHECK(TryParse(cx, "1", filter)); 1.355 + CHECK(TryParse(cx, "1.75", filter)); 1.356 + CHECK(TryParse(cx, "[]", filter)); 1.357 + CHECK(TryParse(cx, "[1]", filter)); 1.358 + CHECK(TryParse(cx, "{}", filter)); 1.359 + return true; 1.360 +} 1.361 + 1.362 +template<size_t N> inline bool 1.363 +TryParse(JSContext *cx, const char (&input)[N], JS::HandleValue filter) 1.364 +{ 1.365 + AutoInflatedString str(cx); 1.366 + JS::RootedValue v(cx); 1.367 + str = input; 1.368 + CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v)); 1.369 + CHECK_SAME(v, JSVAL_NULL); 1.370 + return true; 1.371 +} 1.372 +END_TEST(testParseJSON_reviver)