testExternalStrings.cpp (7059B)
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 "js/CharacterEncoding.h" // JS::FindSmallestEncoding, JS::SmallestEncoding 6 #include "js/GCAPI.h" // JSExternalStringCallbacks, JS_NewExternalUCString, JS_NewExternalStringLatin1, JS_NewMaybeExternalStringUTF8, JS::AutoRequireNoGC 7 #include "js/String.h" // JS::IsExternalStringLatin1 8 #include "jsapi-tests/tests.h" 9 #include "util/Text.h" 10 11 static const char16_t arr[] = u"hi, don't delete me"; 12 static const size_t arrlen = js_strlen(arr); 13 14 static const char arr2[] = "hi, don't delete me"; 15 static const size_t arrlen2 = js_strlen(arr2); 16 17 static int finalized1 = 0; 18 static int finalized2 = 0; 19 static int finalized3 = 0; 20 static int finalized4 = 0; 21 22 struct ExternalStringCallbacks : public JSExternalStringCallbacks { 23 int* finalizedCount = nullptr; 24 bool isTwoBytes = false; 25 26 explicit ExternalStringCallbacks(int* finalizedCount, bool isTwoBytes) 27 : finalizedCount(finalizedCount), isTwoBytes(isTwoBytes) {} 28 29 void finalize(JS::Latin1Char* chars) const override { 30 MOZ_ASSERT(!isTwoBytes); 31 MOZ_ASSERT(chars == (JS::Latin1Char*)arr2); 32 (*finalizedCount)++; 33 } 34 35 void finalize(char16_t* chars) const override { 36 MOZ_ASSERT(isTwoBytes); 37 MOZ_ASSERT(chars == arr); 38 (*finalizedCount)++; 39 } 40 41 size_t sizeOfBuffer(const JS::Latin1Char* chars, 42 mozilla::MallocSizeOf mallocSizeOf) const override { 43 MOZ_CRASH("Unexpected call"); 44 } 45 46 size_t sizeOfBuffer(const char16_t* chars, 47 mozilla::MallocSizeOf mallocSizeOf) const override { 48 MOZ_CRASH("Unexpected call"); 49 } 50 }; 51 52 MOZ_RUNINIT static const ExternalStringCallbacks callbacks1(&finalized1, true); 53 MOZ_RUNINIT static const ExternalStringCallbacks callbacks2(&finalized2, true); 54 MOZ_RUNINIT static const ExternalStringCallbacks callbacks3(&finalized3, false); 55 MOZ_RUNINIT static const ExternalStringCallbacks callbacks4(&finalized4, false); 56 57 BEGIN_TEST(testExternalStrings) { 58 const unsigned N = 1000; 59 60 for (unsigned i = 0; i < N; ++i) { 61 CHECK(JS_NewExternalUCString(cx, arr, arrlen, &callbacks1)); 62 CHECK(JS_NewExternalUCString(cx, arr, arrlen, &callbacks2)); 63 CHECK(JS_NewExternalStringLatin1(cx, (JS::Latin1Char*)arr2, arrlen2, 64 &callbacks3)); 65 CHECK(JS_NewExternalStringLatin1(cx, (JS::Latin1Char*)arr2, arrlen2, 66 &callbacks4)); 67 } 68 69 JS_GC(cx); 70 71 CHECK((N - finalized1) == 0); 72 CHECK((N - finalized2) == 0); 73 CHECK((N - finalized3) == 0); 74 CHECK((N - finalized4) == 0); 75 76 return true; 77 } 78 END_TEST(testExternalStrings) 79 80 struct SimpleExternalStringCallbacks : public JSExternalStringCallbacks { 81 SimpleExternalStringCallbacks() = default; 82 83 void finalize(JS::Latin1Char* chars) const override {} 84 85 void finalize(char16_t* chars) const override { 86 MOZ_CRASH("Unexpected call"); 87 } 88 89 size_t sizeOfBuffer(const JS::Latin1Char* chars, 90 mozilla::MallocSizeOf mallocSizeOf) const override { 91 MOZ_CRASH("Unexpected call"); 92 } 93 94 size_t sizeOfBuffer(const char16_t* chars, 95 mozilla::MallocSizeOf mallocSizeOf) const override { 96 MOZ_CRASH("Unexpected call"); 97 } 98 }; 99 100 static const SimpleExternalStringCallbacks simpleCallback; 101 102 static const char utf8ASCII[] = "hi, I'm UTF-8 string"; 103 static const size_t utf8ASCIILen = js_strlen(utf8ASCII); 104 105 static const char utf8Latin1[] = "hi, I'm \xC3\x9CTF-8 string"; 106 static const size_t utf8Latin1Len = js_strlen(utf8Latin1); 107 108 static const char latin1[] = "hi, I'm \xDCTF-8 string"; 109 static const size_t latin1Len = js_strlen(latin1); 110 111 static const char utf8UTF16[] = "hi, I'm UTF-\xEF\xBC\x98 string"; 112 static const size_t utf8UTF16Len = js_strlen(utf8UTF16); 113 114 static const char16_t utf16[] = u"hi, I'm UTF-8 string"; 115 static const size_t utf16Len = js_strlen(utf16); 116 117 BEGIN_TEST(testExternalStringsUTF8) { 118 // UTF-8 chars with ASCII range content should be converted into external 119 // string. 120 { 121 JS::UTF8Chars utf8Chars(utf8ASCII, utf8ASCIILen); 122 CHECK(JS::FindSmallestEncoding(utf8Chars) == JS::SmallestEncoding::ASCII); 123 bool allocatedExternal = false; 124 JS::Rooted<JSString*> str( 125 cx, JS_NewMaybeExternalStringUTF8(cx, utf8Chars, &simpleCallback, 126 &allocatedExternal)); 127 CHECK(str); 128 CHECK(allocatedExternal); 129 130 const JSExternalStringCallbacks* callbacks = nullptr; 131 const JS::Latin1Char* chars = nullptr; 132 CHECK(JS::IsExternalStringLatin1(str, &callbacks, &chars)); 133 CHECK(callbacks == &simpleCallback); 134 CHECK((void*)chars == (void*)utf8ASCII); 135 136 CHECK(StringHasLatin1Chars(str)); 137 138 JS::AutoAssertNoGC nogc(cx); 139 size_t length; 140 chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length); 141 CHECK(length == utf8ASCIILen); 142 CHECK(memcmp(chars, utf8ASCII, length) == 0); 143 } 144 145 // UTF-8 chars with latin-1 range content shouldn't be converted into external 146 // string, but regular latin-1 string. 147 { 148 JS::UTF8Chars utf8Chars(utf8Latin1, utf8Latin1Len); 149 CHECK(JS::FindSmallestEncoding(utf8Chars) == JS::SmallestEncoding::Latin1); 150 bool allocatedExternal = false; 151 JS::Rooted<JSString*> str( 152 cx, JS_NewMaybeExternalStringUTF8(cx, utf8Chars, &simpleCallback, 153 &allocatedExternal)); 154 CHECK(str); 155 CHECK(!allocatedExternal); 156 157 const JSExternalStringCallbacks* callbacks = nullptr; 158 const JS::Latin1Char* chars = nullptr; 159 CHECK(!JS::IsExternalStringLatin1(str, &callbacks, &chars)); 160 CHECK(!callbacks); 161 CHECK(!chars); 162 163 CHECK(StringHasLatin1Chars(str)); 164 165 JS::AutoAssertNoGC nogc(cx); 166 size_t length; 167 chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length); 168 CHECK(length == latin1Len); 169 CHECK(memcmp(chars, latin1, length) == 0); 170 } 171 172 // UTF-8 chars with UTF-16 range content shouldn't be converted into external 173 // string, but regular TwoBytes string. 174 { 175 JS::UTF8Chars utf8Chars(utf8UTF16, utf8UTF16Len); 176 CHECK(JS::FindSmallestEncoding(utf8Chars) == JS::SmallestEncoding::UTF16); 177 bool allocatedExternal = false; 178 JS::Rooted<JSString*> str( 179 cx, JS_NewMaybeExternalStringUTF8(cx, utf8Chars, &simpleCallback, 180 &allocatedExternal)); 181 CHECK(str); 182 CHECK(!allocatedExternal); 183 184 const JSExternalStringCallbacks* callbacks = nullptr; 185 const JS::Latin1Char* chars = nullptr; 186 CHECK(!JS::IsExternalStringLatin1(str, &callbacks, &chars)); 187 CHECK(!callbacks); 188 CHECK(!chars); 189 190 CHECK(!StringHasLatin1Chars(str)); 191 192 JS::AutoAssertNoGC nogc(cx); 193 size_t length; 194 const char16_t* chars16 = nullptr; 195 chars16 = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &length); 196 CHECK(length == utf16Len); 197 CHECK(memcmp(chars16, utf16, length * sizeof(char16_t)) == 0); 198 } 199 200 return true; 201 } 202 END_TEST(testExternalStringsUTF8)