Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/jsapi-tests/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 10 kB image not shown  

Quelle  testParseJSON.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include <limits>
#include <string.h>

#include "js/Array.h"  // JS::IsArrayObject
#include "js/Exception.h"
#include "js/friend/ErrorMessages.h"  // JSMSG_*
#include "js/JSON.h"
#include "js/MemoryFunctions.h"
#include "js/Printf.h"
#include "js/PropertyAndElement.h"  // JS_GetProperty
#include "jsapi-tests/tests.h"

using namespace js;

class AutoInflatedString {
  JSContext* const cx;
  char16_t* chars_;
  size_t length_;

 public:
  explicit AutoInflatedString(JSContext* cx)
      : cx(cx), chars_(nullptr), length_(0) {}
  ~AutoInflatedString() { JS_free(cx, chars_); }

  template <size_t N>
  void operator=(const char (&str)[N]) {
    length_ = N - 1;
    chars_ = InflateString(cx, str, length_);
    if (!chars_) {
      abort();
    }
  }

  void operator=(const char* str) {
    length_ = strlen(str);
    chars_ = InflateString(cx, str, length_);
    if (!chars_) {
      abort();
    }
  }

  const char16_t* chars() const { return chars_; }
  size_t length() const { return length_; }
};

BEGIN_TEST(testParseJSON_success) {
  // Primitives
  JS::RootedValue expected(cx);
  expected = JS::TrueValue();
  CHECK(TryParse(cx, "true", expected));

  expected = JS::FalseValue();
  CHECK(TryParse(cx, "false", expected));

  expected = JS::NullValue();
  CHECK(TryParse(cx, "null", expected));

  expected.setInt32(0);
  CHECK(TryParse(cx, "0", expected));

  expected.setInt32(1);
  CHECK(TryParse(cx, "1", expected));

  expected.setInt32(-1);
  CHECK(TryParse(cx, "-1", expected));

  expected.setDouble(1);
  CHECK(TryParse(cx, "1", expected));

  expected.setDouble(1.75);
  CHECK(TryParse(cx, "1.75", expected));

  expected.setDouble(9e9);
  CHECK(TryParse(cx, "9e9", expected));

  expected.setDouble(std::numeric_limits<double>::infinity());
  CHECK(TryParse(cx, "9e99999", expected));

  JS::Rooted<JSLinearString*> str(cx);

  const char16_t emptystr[] = {'\0'};
  str = js::NewStringCopyN<CanGC>(cx, emptystr, 0);
  CHECK(str);
  expected = JS::StringValue(str);
  CHECK(TryParse(cx, "\"\"", expected));

  const char16_t nullstr[] = {'\0'};
  str = NewString(cx, nullstr);
  CHECK(str);
  expected = JS::StringValue(str);
  CHECK(TryParse(cx, "\"\\u0000\"", expected));

  const char16_t backstr[] = {'\b'};
  str = NewString(cx, backstr);
  CHECK(str);
  expected = JS::StringValue(str);
  CHECK(TryParse(cx, "\"\\b\"", expected));
  CHECK(TryParse(cx, "\"\\u0008\"", expected));

  const char16_t newlinestr[] = {
      '\n',
  };
  str = NewString(cx, newlinestr);
  CHECK(str);
  expected = JS::StringValue(str);
  CHECK(TryParse(cx, "\"\\n\"", expected));
  CHECK(TryParse(cx, "\"\\u000A\"", expected));

  // Arrays
  JS::RootedValue v(cx), v2(cx);
  JS::RootedObject obj(cx);

  bool isArray;

  CHECK(Parse(cx, "[]", &v));
  CHECK(v.isObject());
  obj = &v.toObject();
  CHECK(JS::IsArrayObject(cx, obj, &isArray));
  CHECK(isArray);
  CHECK(JS_GetProperty(cx, obj, "length", &v2));
  CHECK(v2.isInt32(0));

  CHECK(Parse(cx, "[1]", &v));
  CHECK(v.isObject());
  obj = &v.toObject();
  CHECK(JS::IsArrayObject(cx, obj, &isArray));
  CHECK(isArray);
  CHECK(JS_GetProperty(cx, obj, "0", &v2));
  CHECK(v2.isInt32(1));
  CHECK(JS_GetProperty(cx, obj, "length", &v2));
  CHECK(v2.isInt32(1));

  // Objects
  CHECK(Parse(cx, "{}", &v));
  CHECK(v.isObject());
  obj = &v.toObject();
  CHECK(JS::IsArrayObject(cx, obj, &isArray));
  CHECK(!isArray);

  CHECK(Parse(cx, "{ \"f\": 17 }", &v));
  CHECK(v.isObject());
  obj = &v.toObject();
  CHECK(JS::IsArrayObject(cx, obj, &isArray));
  CHECK(!isArray);
  CHECK(JS_GetProperty(cx, obj, "f", &v2));
  CHECK(v2.isInt32(17));

  return true;
}

template <size_t N>
static JSLinearString* NewString(JSContext* cx, const char16_t (&chars)[N]) {
  return js::NewStringCopyN<CanGC>(cx, chars, N);
}

template <size_t N>
inline bool Parse(JSContext* cx, const char (&input)[N],
                  JS::MutableHandleValue vp) {
  AutoInflatedString str(cx);
  str = input;
  CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp));
  return true;
}

template <size_t N>
inline bool TryParse(JSContext* cx, const char (&input)[N],
                     JS::HandleValue expected) {
  AutoInflatedString str(cx);
  RootedValue v(cx);
  str = input;
  CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v));
  CHECK_SAME(v, expected);
  return true;
}
END_TEST(testParseJSON_success)

BEGIN_TEST(testParseJSON_error) {
  CHECK(Error(cx, "", 1, 1));
  CHECK(Error(cx, "\n", 2, 1));
  CHECK(Error(cx, "\r", 2, 1));
  CHECK(Error(cx, "\r\n", 2, 1));

  CHECK(Error(cx, "[", 1, 2));
  CHECK(Error(cx, "[,]", 1, 2));
  CHECK(Error(cx, "[1,]", 1, 4));
  CHECK(Error(cx, "{a:2}", 1, 2));
  CHECK(Error(cx, "{\"a\":2,}", 1, 8));
  CHECK(Error(cx, "]", 1, 1));
  CHECK(Error(cx, "\"", 1, 2));
  CHECK(Error(cx, "{]", 1, 2));
  CHECK(Error(cx, "[}", 1, 2));
  CHECK(Error(cx, "'wrongly-quoted string'", 1, 1));

  CHECK(Error(cx, "{\"a\":2 \n b:3}", 2, 2));
  CHECK(Error(cx, "\n[", 2, 2));
  CHECK(Error(cx, "\n[,]", 2, 2));
  CHECK(Error(cx, "\n[1,]", 2, 4));
  CHECK(Error(cx, "\n{a:2}", 2, 2));
  CHECK(Error(cx, "\n{\"a\":2,}", 2, 8));
  CHECK(Error(cx, "\n]", 2, 1));
  CHECK(Error(cx, "\"bad string\n\"", 1, 12));
  CHECK(Error(cx, "\r'wrongly-quoted string'", 2, 1));
  CHECK(Error(cx, "\n\"", 2, 2));
  CHECK(Error(cx, "\n{]", 2, 2));
  CHECK(Error(cx, "\n[}", 2, 2));
  CHECK(Error(cx, "{\"a\":[2,3],\n\"b\":,5,6}", 2, 5));

  CHECK(Error(cx, "{\"a\":2 \r b:3}", 2, 2));
  CHECK(Error(cx, "\r[", 2, 2));
  CHECK(Error(cx, "\r[,]", 2, 2));
  CHECK(Error(cx, "\r[1,]", 2, 4));
  CHECK(Error(cx, "\r{a:2}", 2, 2));
  CHECK(Error(cx, "\r{\"a\":2,}", 2, 8));
  CHECK(Error(cx, "\r]", 2, 1));
  CHECK(Error(cx, "\"bad string\r\"", 1, 12));
  CHECK(Error(cx, "\r'wrongly-quoted string'", 2, 1));
  CHECK(Error(cx, "\r\"", 2, 2));
  CHECK(Error(cx, "\r{]", 2, 2));
  CHECK(Error(cx, "\r[}", 2, 2));
  CHECK(Error(cx, "{\"a\":[2,3],\r\"b\":,5,6}", 2, 5));

  CHECK(Error(cx, "{\"a\":2 \r\n b:3}", 2, 2));
  CHECK(Error(cx, "\r\n[", 2, 2));
  CHECK(Error(cx, "\r\n[,]", 2, 2));
  CHECK(Error(cx, "\r\n[1,]", 2, 4));
  CHECK(Error(cx, "\r\n{a:2}", 2, 2));
  CHECK(Error(cx, "\r\n{\"a\":2,}", 2, 8));
  CHECK(Error(cx, "\r\n]", 2, 1));
  CHECK(Error(cx, "\"bad string\r\n\"", 1, 12));
  CHECK(Error(cx, "\r\n'wrongly-quoted string'", 2, 1));
  CHECK(Error(cx, "\r\n\"", 2, 2));
  CHECK(Error(cx, "\r\n{]", 2, 2));
  CHECK(Error(cx, "\r\n[}", 2, 2));
  CHECK(Error(cx, "{\"a\":[2,3],\r\n\"b\":,5,6}", 2, 5));

  CHECK(Error(cx, "\n\"bad string\n\"", 2, 12));
  CHECK(Error(cx, "\r\"bad string\r\"", 2, 12));
  CHECK(Error(cx, "\r\n\"bad string\r\n\"", 2, 12));

  CHECK(Error(cx, "{\n\"a\":[2,3],\r\"b\":,5,6}", 3, 5));
  CHECK(Error(cx, "{\r\"a\":[2,3],\n\"b\":,5,6}", 3, 5));
  CHECK(Error(cx, "[\"\\t\\q", 1, 6));
  CHECK(Error(cx, "[\"\\t\x00", 1, 5));
  CHECK(Error(cx, "[\"\\t\x01", 1, 5));
  CHECK(Error(cx, "[\"\\t\\\x00", 1, 6));
  CHECK(Error(cx, "[\"\\t\\\x01", 1, 6));

  // Unicode escape errors are messy.  The first bad character could be
  // non-hexadecimal, or it could be absent entirely.  Include tests where
  // there's a bad character, followed by zero to as many characters as are
  // needed to form a complete Unicode escape sequence, plus one.  (The extra
  // characters beyond are valuable because our implementation checks for
  // too-few subsequent characters first, before checking for subsequent
  // non-hexadecimal characters.  So \u<END>, \u0<END>, \u00<END>, and
  // \u000<END> are all *detected* as invalid by the same code path, but the
  // process of computing the first invalid character follows a different
  // code path for each.  And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected
  // as invalid by the same code path [ignoring which precise subexpression
  // triggers failure of a single condition], but the computation of the
  // first invalid character follows a different code path for each.)
  CHECK(Error(cx, "[\"\\t\\u", 1, 7));
  CHECK(Error(cx, "[\"\\t\\uZ", 1, 7));
  CHECK(Error(cx, "[\"\\t\\uZZ", 1, 7));
  CHECK(Error(cx, "[\"\\t\\uZZZ", 1, 7));
  CHECK(Error(cx, "[\"\\t\\uZZZZ", 1, 7));
  CHECK(Error(cx, "[\"\\t\\uZZZZZ", 1, 7));

  CHECK(Error(cx, "[\"\\t\\u0", 1, 8));
  CHECK(Error(cx, "[\"\\t\\u0Z", 1, 8));
  CHECK(Error(cx, "[\"\\t\\u0ZZ", 1, 8));
  CHECK(Error(cx, "[\"\\t\\u0ZZZ", 1, 8));
  CHECK(Error(cx, "[\"\\t\\u0ZZZZ", 1, 8));

  CHECK(Error(cx, "[\"\\t\\u00", 1, 9));
  CHECK(Error(cx, "[\"\\t\\u00Z", 1, 9));
  CHECK(Error(cx, "[\"\\t\\u00ZZ", 1, 9));
  CHECK(Error(cx, "[\"\\t\\u00ZZZ", 1, 9));

  CHECK(Error(cx, "[\"\\t\\u000", 1, 10));
  CHECK(Error(cx, "[\"\\t\\u000Z", 1, 10));
  CHECK(Error(cx, "[\"\\t\\u000ZZ", 1, 10));

  return true;
}

template <size_t N>
inline bool Error(JSContext* cx, const char (&input)[N], uint32_t expectedLine,
                  uint32_t expectedColumn) {
  AutoInflatedString str(cx);
  RootedValue dummy(cx);
  str = input;

  bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
  CHECK(!ok);

  JS::ExceptionStack exnStack(cx);
  CHECK(StealPendingExceptionStack(cx, &exnStack));

  JS::ErrorReportBuilder report(cx);
  CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects));
  CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE);

  UniqueChars lineAndColumnASCII =
      JS_smprintf("line %d column %d", expectedLine, expectedColumn);
  CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII.get()) !=
        nullptr);

  /* We do not execute JS, so there should be no exception thrown. */
  CHECK(!JS_IsExceptionPending(cx));

  return true;
}
END_TEST(testParseJSON_error)

static bool Censor(JSContext* cx, unsigned argc, JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  const unsigned expectedArgCount =
      JS::Prefs::experimental_json_parse_with_source() ? 3 : 2;
  MOZ_RELEASE_ASSERT(args.length() == expectedArgCount);
  MOZ_RELEASE_ASSERT(args[0].isString());
  args.rval().setNull();
  return true;
}

BEGIN_TEST(testParseJSON_reviver) {
  JSFunction* fun = JS_NewFunction(cx, Censor, 0, 0, "censor");
  CHECK(fun);

  JS::RootedValue filter(cx, JS::ObjectValue(*JS_GetFunctionObject(fun)));

  CHECK(TryParse(cx, "true", filter));
  CHECK(TryParse(cx, "false", filter));
  CHECK(TryParse(cx, "null", filter));
  CHECK(TryParse(cx, "1", filter));
  CHECK(TryParse(cx, "1.75", filter));
  CHECK(TryParse(cx, "[]", filter));
  CHECK(TryParse(cx, "[1]", filter));
  CHECK(TryParse(cx, "{}", filter));
  return true;
}

template <size_t N>
inline bool TryParse(JSContext* cx, const char (&input)[N],
                     JS::HandleValue filter) {
  AutoInflatedString str(cx);
  JS::RootedValue v(cx);
  str = input;
  CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v));
  CHECK(v.isNull());
  return true;
}
END_TEST(testParseJSON_reviver)

Messung V0.5
C=95 H=90 G=92

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.