diff options
Diffstat (limited to 'js/src/jsapi-tests/testFrontendJSON.cpp')
-rw-r--r-- | js/src/jsapi-tests/testFrontendJSON.cpp | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testFrontendJSON.cpp b/js/src/jsapi-tests/testFrontendJSON.cpp new file mode 100644 index 0000000000..b5315e6084 --- /dev/null +++ b/js/src/jsapi-tests/testFrontendJSON.cpp @@ -0,0 +1,646 @@ +/* -*- 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; + } + + 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) |