tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)