js/src/jsapi-tests/testParseJSON.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial