/* -*- 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 "mozilla/Maybe.h" // mozilla::Maybe
#include <string>
#include "js/AllocPolicy.h" // js::SystemAllocPolicy
#include "js/JSON.h"
#include "js/Vector.h" // js::Vector
#include "jsapi-tests/tests.h"
using namespace JS;
BEGIN_FRONTEND_TEST(testIsValidJSONLatin1) {
const char * source;
source = "true" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "false" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "null" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "0" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "1" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "-1" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "1.75" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "9000000000" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "\" foo\"" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "[]" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "[1, true]" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "{}" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "{\" key\": 10}" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "{\" key\": 10, \" prop\": 20}" ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "1 " ;
CHECK(IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
// Invalid cases.
source = "" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "1 1" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = ".1" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "undefined" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "TRUE" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "'foo'" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "[" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "{" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
source = "/a/" ;
CHECK(!IsValidJSON(reinterpret_cast <const JS::Latin1Char*>(source),
strlen(source)));
return true ;
}
END_TEST(testIsValidJSONLatin1)
BEGIN_FRONTEND_TEST(testIsValidJSONTwoBytes) {
const char16_t* source;
source = u"true" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"false" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"null" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"0" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"1" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"-1" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"1.75" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"9000000000" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"\" foo\"" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"[]" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"[1, true]" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"{}" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"{\" key\": 10}" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"{\" key\": 10, \" prop\": 20}" ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"1 " ;
CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source)));
// Invalid cases.
source = u"" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"1 1" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u".1" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"undefined" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"TRUE" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"'foo'" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"[" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"{" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
source = u"/a/" ;
CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source)));
return true ;
}
END_TEST(testIsValidJSONTwoBytes)
BEGIN_FRONTEND_TEST(testParseJSONWithHandler) {
{
MyHandler handler;
const char * source =
"{ \" prop1\": 10.5, \" prop\\uff12\": [true, false, null, \" Ascii\", "
"\" \\u3042\\u3044\\u3046\", \" \\u0020\", \" \\u0080\"] }" ;
CHECK(JS::ParseJSONWithHandler((const JS::Latin1Char*)source,
std::char_traits<char >::length(source),
&handler));
size_t i = 0;
CHECK(handler.events[i++] == MyHandler::Event::StartObject);
// Non-escaped ASCII property name in Latin1 input should be passed with
// Latin1.
CHECK(handler.events[i++] == MyHandler::Event::Latin1Prop1);
CHECK(handler.events[i++] == MyHandler::Event::Number);
// Escaped non-Latin1 property name in Latin1 input should be passed with
// TwoBytes.
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2);
CHECK(handler.events[i++] == MyHandler::Event::StartArray);
CHECK(handler.events[i++] == MyHandler::Event::True );
CHECK(handler.events[i++] == MyHandler::Event::False );
CHECK(handler.events[i++] == MyHandler::Event::Null);
// Non-escaped ASCII string in Latin1 input should be passed with Latin1.
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str1);
// Escaped non-Latin1 string in Latin1 input should be passed with TwoBytes.
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2);
// Escaped ASCII-range string in Latin1 input should be passed with Latin1.
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3);
// Escaped Latin1-range string in Latin1 input should be passed with Latin1.
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4);
CHECK(handler.events[i++] == MyHandler::Event::EndArray);
CHECK(handler.events[i++] == MyHandler::Event::EndObject);
CHECK(handler.events.length() == i);
}
{
MyHandler handler;
const char * source = "{" ;
CHECK(!JS::ParseJSONWithHandler((const JS::Latin1Char*)source,
std::char_traits<char >::length(source),
&handler));
size_t i = 0;
CHECK(handler.events[i++] == MyHandler::Event::StartObject);
CHECK(handler.events[i++] == MyHandler::Event::Error);
CHECK(handler.events.length() == i);
}
{
MyHandler handler;
const char16_t* source =
u"{ \" prop1\": 10.5, \" prop\uff12\": [true, false, null, \" Ascii\", "
u"\" \\u3042\\u3044\\u3046\", \" \\u0020\", \" \\u0080\"] }" ;
CHECK(JS::ParseJSONWithHandler(
source, std::char_traits<char16_t>::length(source), &handler));
size_t i = 0;
CHECK(handler.events[i++] == MyHandler::Event::StartObject);
// Non-escaped ASCII property name in TwoBytes input should be passed with
// TwoBytes.
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp1);
CHECK(handler.events[i++] == MyHandler::Event::Number);
// Escaped non-Latin1 property name in TwoBytes input should be passed with
// TwoBytes.
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2);
CHECK(handler.events[i++] == MyHandler::Event::StartArray);
CHECK(handler.events[i++] == MyHandler::Event::True );
CHECK(handler.events[i++] == MyHandler::Event::False );
CHECK(handler.events[i++] == MyHandler::Event::Null);
// Non-escaped ASCII string in TwoBytes input should be passed with
// TwoBytes.
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr1);
// Escaped non-Latin1 string in TwoBytes input should be passed with
// TwoBytes.
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2);
// Escaped ASCII-range string in TwoBytes input should be passed with
// Latin1.
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3);
// Escaped Latin1-range string in TwoBytes input should be passed with
// Latin1.
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4);
CHECK(handler.events[i++] == MyHandler::Event::EndArray);
CHECK(handler.events[i++] == MyHandler::Event::EndObject);
CHECK(handler.events.length() == i);
}
{
MyHandler handler;
const char16_t* source = u"{" ;
CHECK(!JS::ParseJSONWithHandler(
source, std::char_traits<char16_t>::length(source), &handler));
size_t i = 0;
CHECK(handler.events[i++] == MyHandler::Event::StartObject);
CHECK(handler.events[i++] == MyHandler::Event::Error);
CHECK(handler.events.length() == i);
}
// Verify the failure case is handled properly and no methods are called
// after the failure.
bool checkedLast = false ;
for (size_t failAt = 1; !checkedLast; failAt++) {
MyHandler handler;
handler.failAt.emplace(failAt);
const char * source =
"{ \" prop1\": 10.5, \" prop\\uff12\": [true, false, null, \" Ascii\", "
"\" \\u3042\\u3044\\u3046\", \" \\u0020\", \" \\u0080\"] }" ;
CHECK(!JS::ParseJSONWithHandler((const JS::Latin1Char*)source,
std::char_traits<char >::length(source),
&handler));
CHECK(handler.events.length() == failAt);
size_t i = 0;
CHECK(handler.events[i++] == MyHandler::Event::StartObject);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Latin1Prop1);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Number);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::StartArray);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::True );
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::False );
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Null);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str1);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::EndArray);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::EndObject);
checkedLast = true ;
}
checkedLast = false ;
for (size_t failAt = 1; !checkedLast; failAt++) {
MyHandler handler;
handler.failAt.emplace(failAt);
const char16_t* source =
u"{ \" prop1\": 10.5, \" prop\uff12\": [true, false, null, \" Ascii\", "
u"\" \\u3042\\u3044\\u3046\", \" \\u0020\", \" \\u0080\"] }" ;
CHECK(!JS::ParseJSONWithHandler(
source, std::char_traits<char16_t>::length(source), &handler));
CHECK(handler.events.length() == failAt);
size_t i = 0;
CHECK(handler.events[i++] == MyHandler::Event::StartObject);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp1);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Number);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::StartArray);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::True );
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::False );
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Null);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr1);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::EndArray);
if (i >= failAt) {
continue ;
}
CHECK(handler.events[i++] == MyHandler::Event::EndObject);
checkedLast = true ;
}
{
const size_t failAt = 1;
MyHandler handler;
const char16_t* source;
#define IMMEDIATE_FAIL(json) \
handler.failAt.emplace(failAt); \
source = json; \
CHECK(!JS::ParseJSONWithHandler( \
source, std::char_traits<char16_t>::length(source), &handler)); \
CHECK(handler.events.length() == failAt); \
handler.events.clear();
IMMEDIATE_FAIL(u"{" );
IMMEDIATE_FAIL(u"[" );
IMMEDIATE_FAIL(u"\" string\"" );
IMMEDIATE_FAIL(u"1" );
IMMEDIATE_FAIL(u"true" );
IMMEDIATE_FAIL(u"null" );
#undef IMMEDIATE_FAIL
}
return true ;
}
class MyHandler : public JS::JSONParseHandler {
public :
enum class Event {
Uninitialized = 0,
StartObject,
Latin1Prop1,
TwoBytesProp1,
Number,
TwoBytesProp2,
StartArray,
True ,
False ,
Null,
Latin1Str1,
TwoBytesStr1,
TwoBytesStr2,
Latin1Str3,
Latin1Str4,
EndArray,
EndObject,
Error,
UnexpectedNumber,
UnexpectedLatin1Prop,
UnexpectedTwoBytesProp,
UnexpectedLatin1String,
UnexpectedTwoBytesString,
};
js::Vector<Event, 0, js::SystemAllocPolicy> events;
mozilla::Maybe<size_t> failAt;
MyHandler() {}
virtual ~MyHandler() {}
bool startObject() override {
MOZ_ALWAYS_TRUE(events.append(Event::StartObject));
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool propertyName(const JS::Latin1Char* name, size_t length) override {
if (length == 5 && name[0] == 'p' && name[1] == 'r' && name[2] == 'o' &&
name[3] == 'p' && name[4] == '1' ) {
MOZ_ALWAYS_TRUE(events.append(Event::Latin1Prop1));
} else {
MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedLatin1Prop));
}
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool propertyName(const char16_t* name, size_t length) override {
if (length == 5 && name[0] == 'p' && name[1] == 'r' && name[2] == 'o' &&
name[3] == 'p' && name[4] == '1' ) {
MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesProp1));
} else if (length == 5 && name[0] == 'p' && name[1] == 'r' &&
name[2] == 'o' && name[3] == 'p' && name[4] == 0xff12) {
MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesProp2));
} else {
MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedTwoBytesProp));
}
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool endObject() override {
MOZ_ALWAYS_TRUE(events.append(Event::EndObject));
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool startArray() override {
MOZ_ALWAYS_TRUE(events.append(Event::StartArray));
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool endArray() override {
MOZ_ALWAYS_TRUE(events.append(Event::EndArray));
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool stringValue(const JS::Latin1Char* name, size_t length) override {
if (length == 5 && name[0] == 'A' && name[1] == 's' && name[2] == 'c' &&
name[3] == 'i' && name[4] == 'i' ) {
MOZ_ALWAYS_TRUE(events.append(Event::Latin1Str1));
} else if (length == 1 && name[0] == ' ' ) {
MOZ_ALWAYS_TRUE(events.append(Event::Latin1Str3));
} else if (length == 1 && name[0] == 0x80) {
MOZ_ALWAYS_TRUE(events.append(Event::Latin1Str4));
} else {
MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedLatin1String));
}
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool stringValue(const char16_t* name, size_t length) override {
if (length == 5 && name[0] == 'A' && name[1] == 's' && name[2] == 'c' &&
name[3] == 'i' && name[4] == 'i' ) {
MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesStr1));
} else if (length == 3 && name[0] == 0x3042 && name[1] == 0x3044 &&
name[2] == 0x3046) {
MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesStr2));
} else {
MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedTwoBytesString));
}
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool numberValue(double d) override {
if (d == 10.5) {
MOZ_ALWAYS_TRUE(events.append(Event::Number));
} else {
MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedNumber));
}
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool booleanValue(bool v) override {
if (v) {
MOZ_ALWAYS_TRUE(events.append(Event::True ));
} else {
MOZ_ALWAYS_TRUE(events.append(Event::False ));
}
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
bool nullValue() override {
MOZ_ALWAYS_TRUE(events.append(Event::Null));
if (failAt.isSome() && events.length() == *failAt) {
failAt.reset();
return false ;
}
return true ;
}
void error(const char * msg, uint32_t line, uint32_t column) override {
MOZ_ALWAYS_TRUE(events.append(Event::Error));
}
};
END_TEST(testParseJSONWithHandler)
quality 99%
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland