testEmptyWindowIsOmitted.cpp (5131B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "mozilla/TextUtils.h" 6 #include "mozilla/Utf8.h" 7 8 #include <cstring> 9 10 #include "js/CharacterEncoding.h" 11 #include "js/CompilationAndEvaluation.h" // JS::Compile 12 #include "js/Exception.h" 13 #include "js/friend/ErrorMessages.h" // JSMSG_* 14 #include "js/SourceText.h" 15 #include "jsapi-tests/tests.h" 16 #include "util/Text.h" 17 #include "vm/ErrorReporting.h" 18 19 using mozilla::IsAsciiHexDigit; 20 using mozilla::Utf8Unit; 21 22 BEGIN_TEST(testEmptyWindow) { return testUtf8() && testUtf16(); } 23 24 bool testUtf8() { 25 // Bad unit with nothing before it. 26 static const char badLeadingUnit[] = "\x80"; 27 CHECK(testOmittedWindow(badLeadingUnit, JSMSG_BAD_LEADING_UTF8_UNIT, "0x80")); 28 29 // Bad unit at start of a fresh line. 30 static const char badStartingFreshLine[] = "var x = 5;\n\x98"; 31 CHECK(testOmittedWindow(badStartingFreshLine, JSMSG_BAD_LEADING_UTF8_UNIT, 32 "0x98")); 33 34 // Bad trailing unit in initial code point. 35 static const char badTrailingUnit[] = "\xD8\x20"; 36 CHECK(testOmittedWindow(badTrailingUnit, JSMSG_BAD_TRAILING_UTF8_UNIT, 37 "0xD8 0x20")); 38 39 // Bad trailing unit at start of a fresh line. 40 static const char badTrailingUnitFreshLine[] = "var x = 5;\n\xD8\x20"; 41 CHECK(testOmittedWindow(badTrailingUnitFreshLine, 42 JSMSG_BAD_TRAILING_UTF8_UNIT, "0xD8 0x20")); 43 44 // Overlong in initial code point. 45 static const char overlongInitial[] = "\xC0\x80"; 46 CHECK(testOmittedWindow(overlongInitial, JSMSG_FORBIDDEN_UTF8_CODE_POINT, 47 "0xC0 0x80")); 48 49 // Overlong at start of a fresh line. 50 static const char overlongFreshLine[] = "var x = 5;\n\xC0\x81"; 51 CHECK(testOmittedWindow(overlongFreshLine, JSMSG_FORBIDDEN_UTF8_CODE_POINT, 52 "0xC0 0x81")); 53 54 // Not-enough in initial code point. 55 static const char notEnoughInitial[] = "\xF0"; 56 CHECK( 57 testOmittedWindow(notEnoughInitial, JSMSG_NOT_ENOUGH_CODE_UNITS, "0xF0")); 58 59 // Not-enough at start of a fresh line. 60 static const char notEnoughFreshLine[] = "var x = 5;\n\xF0"; 61 CHECK(testOmittedWindow(notEnoughFreshLine, JSMSG_NOT_ENOUGH_CODE_UNITS, 62 "0xF0")); 63 64 return true; 65 } 66 67 bool testUtf16() { 68 // Bad unit with nothing before it. 69 static const char16_t badLeadingUnit[] = u"\xDFFF"; 70 CHECK(testOmittedWindow(badLeadingUnit, JSMSG_ILLEGAL_CHARACTER)); 71 72 // Bad unit at start of a fresh line. 73 static const char16_t badStartingFreshLine[] = u"var x = 5;\n\xDFFF"; 74 CHECK(testOmittedWindow(badStartingFreshLine, JSMSG_ILLEGAL_CHARACTER)); 75 76 return true; 77 } 78 79 static bool startsWith(const char* str, const char* prefix) { 80 return std::strncmp(prefix, str, strlen(prefix)) == 0; 81 } 82 83 static bool equals(const char* str, const char* expected) { 84 return std::strcmp(str, expected) == 0; 85 } 86 87 JSScript* compile(const char16_t* chars, size_t len) { 88 JS::SourceText<char16_t> source; 89 MOZ_RELEASE_ASSERT( 90 source.init(cx, chars, len, JS::SourceOwnership::Borrowed)); 91 92 JS::CompileOptions options(cx); 93 return JS::Compile(cx, options, source); 94 } 95 96 JSScript* compile(const char* chars, size_t len) { 97 JS::SourceText<Utf8Unit> source; 98 MOZ_RELEASE_ASSERT( 99 source.init(cx, chars, len, JS::SourceOwnership::Borrowed)); 100 101 JS::CompileOptions options(cx); 102 return JS::Compile(cx, options, source); 103 } 104 105 template <typename CharT, size_t N> 106 bool testOmittedWindow(const CharT (&chars)[N], unsigned expectedErrorNumber, 107 const char* badCodeUnits = nullptr) { 108 JS::Rooted<JSScript*> script(cx, compile(chars, N - 1)); 109 CHECK(!script); 110 111 JS::ExceptionStack exnStack(cx); 112 CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); 113 114 JS::ErrorReportBuilder report(cx); 115 CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); 116 117 const auto* errorReport = report.report(); 118 119 CHECK(errorReport->errorNumber == expectedErrorNumber); 120 121 if (const auto& notes = errorReport->notes) { 122 CHECK(sizeof(CharT) == 1); 123 CHECK(badCodeUnits != nullptr); 124 125 auto iter = notes->begin(); 126 CHECK(iter != notes->end()); 127 128 const char* noteMessage = (*iter)->message().c_str(); 129 130 // The prefix ought always be the same. 131 static constexpr char expectedPrefix[] = 132 "the code units comprising this invalid code point were: "; 133 constexpr size_t expectedPrefixLen = js_strlen(expectedPrefix); 134 135 CHECK(startsWith(noteMessage, expectedPrefix)); 136 137 // The end of the prefix is the bad code units. 138 CHECK(equals(noteMessage + expectedPrefixLen, badCodeUnits)); 139 140 ++iter; 141 CHECK(iter == notes->end()); 142 } else { 143 CHECK(sizeof(CharT) == 2); 144 145 // UTF-16 encoding "errors" are not categorical errors, so the errors 146 // are just of the invalid-character sort, without an accompanying note 147 // spelling out a series of invalid code units. 148 CHECK(!badCodeUnits); 149 } 150 151 CHECK(!errorReport->linebuf()); 152 153 return true; 154 } 155 END_TEST(testEmptyWindow)