StringImpl utf8 conversion should not fail silently.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 May 2018 23:54:16 +0000 (23:54 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 May 2018 23:54:16 +0000 (23:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185888
<rdar://problem/40464506>

Reviewed by Filip Pizlo.

JSTests:

* stress/regress-185888.js: Added.

Source/JavaScriptCore:

* dfg/DFGLazyJSValue.cpp:
(JSC::DFG::LazyJSValue::dumpInContext const):
* runtime/DateConstructor.cpp:
(JSC::constructDate):
(JSC::dateParse):
* runtime/JSDateMath.cpp:
(JSC::parseDate):
* runtime/JSDateMath.h:

Source/WTF:

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/PrintStream.cpp:
(WTF::printExpectedCStringHelper):
(WTF::printInternal):
* wtf/text/StringImpl.cpp:
(WTF::StringImpl::utf8Impl):
(WTF::StringImpl::utf8ForCharacters):
(WTF::StringImpl::tryUtf8ForRange const):
(WTF::StringImpl::tryUtf8 const):
(WTF::StringImpl::utf8 const):
(WTF::StringImpl::utf8ForRange const): Deleted.
* wtf/text/StringImpl.h:
* wtf/text/StringView.cpp:
(WTF::StringView::tryUtf8 const):
(WTF::StringView::utf8 const):
* wtf/text/StringView.h:
* wtf/text/UTF8ConversionError.h: Added.
* wtf/text/WTFString.cpp:
(WTF::String::tryUtf8 const):
(WTF::String::utf8 const):
* wtf/text/WTFString.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@232089 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
JSTests/ChangeLog
JSTests/stress/regress-185888.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGLazyJSValue.cpp
Source/JavaScriptCore/runtime/DateConstructor.cpp
Source/JavaScriptCore/runtime/JSDateMath.cpp
Source/JavaScriptCore/runtime/JSDateMath.h
Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/PrintStream.cpp
Source/WTF/wtf/text/StringImpl.cpp
Source/WTF/wtf/text/StringImpl.h
Source/WTF/wtf/text/StringView.cpp
Source/WTF/wtf/text/StringView.h
Source/WTF/wtf/text/UTF8ConversionError.h [new file with mode: 0644]
Source/WTF/wtf/text/WTFString.cpp
Source/WTF/wtf/text/WTFString.h

index 7224c06..4f4d857 100644 (file)
@@ -1,3 +1,13 @@
+2018-05-22  Mark Lam  <mark.lam@apple.com>
+
+        StringImpl utf8 conversion should not fail silently.
+        https://bugs.webkit.org/show_bug.cgi?id=185888
+        <rdar://problem/40464506>
+
+        Reviewed by Filip Pizlo.
+
+        * stress/regress-185888.js: Added.
+
 2018-05-22  Keith Miller  <keith_miller@apple.com>
 
         We should have a CoW storage for NewArrayBuffer arrays.
diff --git a/JSTests/stress/regress-185888.js b/JSTests/stress/regress-185888.js
new file mode 100644 (file)
index 0000000..c70f7ce
--- /dev/null
@@ -0,0 +1,10 @@
+var exception;
+try {
+    const str = "a".padStart(0x80000000 - 1);
+    new Date(str);
+} catch (e) {
+    exception = e;
+}
+
+if (exception != "Error: Out of memory")
+    throw "FAILED";
index 4fc72fe..20474cd 100644 (file)
@@ -1,3 +1,20 @@
+2018-05-22  Mark Lam  <mark.lam@apple.com>
+
+        StringImpl utf8 conversion should not fail silently.
+        https://bugs.webkit.org/show_bug.cgi?id=185888
+        <rdar://problem/40464506>
+
+        Reviewed by Filip Pizlo.
+
+        * dfg/DFGLazyJSValue.cpp:
+        (JSC::DFG::LazyJSValue::dumpInContext const):
+        * runtime/DateConstructor.cpp:
+        (JSC::constructDate):
+        (JSC::dateParse):
+        * runtime/JSDateMath.cpp:
+        (JSC::parseDate):
+        * runtime/JSDateMath.h:
+
 2018-05-22  Keith Miller  <keith_miller@apple.com>
 
         Remove the UnconditionalFinalizer class
index 5de429a..43bead6 100644 (file)
@@ -250,7 +250,7 @@ void LazyJSValue::dumpInContext(PrintStream& out, DumpContext* context) const
     case SingleCharacterString:
         out.print("Lazy:SingleCharacterString(");
         out.printf("%04X", static_cast<unsigned>(character()));
-        out.print(" / ", StringImpl::utf8ForCharacters(&u.character, 1), ")");
+        out.print(" / ", StringImpl::utf8ForCharacters(&u.character, 1).value(), ")");
         return;
     case KnownStringImpl:
         out.print("Lazy:KnownString(", stringImpl(), ")");
index 38383fc..ce99634 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2004-2008, 2011, 2016 Apple Inc. All rights reserved.
+ *  Copyright (C) 2004-2018 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -136,9 +136,10 @@ JSObject* constructDate(ExecState* exec, JSGlobalObject* globalObject, JSValue n
         else {
             JSValue primitive = arg0.toPrimitive(exec);
             RETURN_IF_EXCEPTION(scope, nullptr);
-            if (primitive.isString())
-                value = parseDate(vm, asString(primitive)->value(exec));
-            else
+            if (primitive.isString()) {
+                value = parseDate(exec, vm, asString(primitive)->value(exec));
+                RETURN_IF_EXCEPTION(scope, nullptr);
+            } else
                 value = primitive.toNumber(exec);
         }
     } else
@@ -172,7 +173,8 @@ EncodedJSValue JSC_HOST_CALL dateParse(ExecState* exec)
     auto scope = DECLARE_THROW_SCOPE(vm);
     String dateStr = exec->argument(0).toWTFString(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    return JSValue::encode(jsNumber(parseDate(vm, dateStr)));
+    scope.release();
+    return JSValue::encode(jsNumber(parseDate(exec, vm, dateStr)));
 }
 
 EncodedJSValue JSC_HOST_CALL dateNow(ExecState*)
index c2bf826..c13b3de 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2006, 2007, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2009 Google Inc. All rights reserved.
  * Copyright (C) 2007-2009 Torch Mobile, Inc.
  * Copyright (C) 2010 &yet, LLC. (nate@andyet.net)
@@ -235,13 +235,26 @@ double parseDateFromNullTerminatedCharacters(VM& vm, const char* dateString)
     return localTimeMS - (offset * WTF::msPerMinute);
 }
 
-double parseDate(VM& vm, const String& date)
+double parseDate(ExecState* exec, VM& vm, const String& date)
 {
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     if (date == vm.cachedDateString)
         return vm.cachedDateStringValue;
-    double value = parseES5DateFromNullTerminatedCharacters(date.utf8().data());
+    auto expectedString = date.tryGetUtf8();
+    if (!expectedString) {
+        if (expectedString.error() == UTF8ConversionError::OutOfMemory)
+            throwOutOfMemoryError(exec, scope);
+        // https://tc39.github.io/ecma262/#sec-date-objects section 20.3.3.2 states that:
+        // "Unrecognizable Strings or dates containing illegal element values in the
+        // format String shall cause Date.parse to return NaN."
+        return std::numeric_limits<double>::quiet_NaN();
+    }
+
+    auto dateUtf8 = expectedString.value();
+    double value = parseES5DateFromNullTerminatedCharacters(dateUtf8.data());
     if (std::isnan(value))
-        value = parseDateFromNullTerminatedCharacters(vm, date.utf8().data());
+        value = parseDateFromNullTerminatedCharacters(vm, dateUtf8.data());
     vm.cachedDateString = date;
     vm.cachedDateStringValue = value;
     return value;
index 5e81382..9d49758 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2009 Google Inc. All rights reserved.
  * Copyright (C) 2010 Research In Motion Limited. All rights reserved.
  *
 
 namespace JSC {
 
+class ExecState;
 class VM;
 
 JS_EXPORT_PRIVATE void msToGregorianDateTime(VM&, double, WTF::TimeType outputTimeType, GregorianDateTime&);
 JS_EXPORT_PRIVATE double gregorianDateTimeToMS(VM&, const GregorianDateTime&, double, WTF::TimeType inputTimeType);
 JS_EXPORT_PRIVATE double getUTCOffset(VM&);
 JS_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(VM&, const char* dateString);
-JS_EXPORT_PRIVATE double parseDate(VM&, const WTF::String&);
+JS_EXPORT_PRIVATE double parseDate(ExecState*, VM&, const WTF::String&);
 
 } // namespace JSC
index 266efe2..1c667d0 100644 (file)
@@ -1,3 +1,34 @@
+2018-05-22  Mark Lam  <mark.lam@apple.com>
+
+        StringImpl utf8 conversion should not fail silently.
+        https://bugs.webkit.org/show_bug.cgi?id=185888
+        <rdar://problem/40464506>
+
+        Reviewed by Filip Pizlo.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/PrintStream.cpp:
+        (WTF::printExpectedCStringHelper):
+        (WTF::printInternal):
+        * wtf/text/StringImpl.cpp:
+        (WTF::StringImpl::utf8Impl):
+        (WTF::StringImpl::utf8ForCharacters):
+        (WTF::StringImpl::tryUtf8ForRange const):
+        (WTF::StringImpl::tryUtf8 const):
+        (WTF::StringImpl::utf8 const):
+        (WTF::StringImpl::utf8ForRange const): Deleted.
+        * wtf/text/StringImpl.h:
+        * wtf/text/StringView.cpp:
+        (WTF::StringView::tryUtf8 const):
+        (WTF::StringView::utf8 const):
+        * wtf/text/StringView.h:
+        * wtf/text/UTF8ConversionError.h: Added.
+        * wtf/text/WTFString.cpp:
+        (WTF::String::tryUtf8 const):
+        (WTF::String::utf8 const):
+        * wtf/text/WTFString.h:
+
 2018-05-22  Chris Dumez  <cdumez@apple.com>
 
         Regression(AsyncPolicyDelegates): Box.app login Window is blank
index aa77fbf..1043b8d 100644 (file)
                FEB6B035201BE0B600B958C1 /* PointerPreparations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointerPreparations.h; sourceTree = "<group>"; };
                FEDACD3B1630F83F00C69634 /* StackStats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StackStats.cpp; sourceTree = "<group>"; };
                FEDACD3C1630F83F00C69634 /* StackStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackStats.h; sourceTree = "<group>"; };
+               FEF295BF20B49DCB00CF283A /* UTF8ConversionError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UTF8ConversionError.h; sourceTree = "<group>"; };
                FF0A436588954F3CB07DBECA /* StdList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StdList.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
                                A8A4732C151A825B004123FF /* TextPosition.h */,
                                A3E4DD911F3A803400DED0B4 /* TextStream.cpp */,
                                A3E4DD921F3A803400DED0B4 /* TextStream.h */,
+                               FEF295BF20B49DCB00CF283A /* UTF8ConversionError.h */,
                                70ECA60C1B02426800449739 /* UniquedStringImpl.h */,
                                A3AB6E6A1F3E1AD6009C14B1 /* ValueToString.h */,
                                A8A4732D151A825B004123FF /* WTFString.cpp */,
index 70dc231..9faec01 100644 (file)
@@ -305,6 +305,7 @@ set(WTF_PUBLIC_HEADERS
     text/TextBreakIteratorInternalICU.h
     text/TextPosition.h
     text/TextStream.h
+    text/UTF8ConversionError.h
     text/UniquedStringImpl.h
     text/ValueToString.h
     text/WTFString.h
index 9e11c99..380c67b 100644 (file)
@@ -82,9 +82,21 @@ void printInternal(PrintStream& out, const char* string)
     out.printf("%s", string);
 }
 
+static void printExpectedCStringHelper(PrintStream& out, const char* type, Expected<CString, UTF8ConversionError> expectedCString)
+{
+    if (UNLIKELY(!expectedCString)) {
+        if (expectedCString.error() == UTF8ConversionError::OutOfMemory)
+            out.print("(Out of memory while converting ", type, " to utf8)");
+        else
+            out.print("(failed to convert ", type, " to utf8)");
+        return;
+    }
+    out.print(expectedCString.value());
+}
+
 void printInternal(PrintStream& out, const StringView& string)
 {
-    out.print(string.utf8());
+    printExpectedCStringHelper(out, "StringView", string.tryGetUtf8());
 }
 
 void printInternal(PrintStream& out, const CString& string)
@@ -94,7 +106,7 @@ void printInternal(PrintStream& out, const CString& string)
 
 void printInternal(PrintStream& out, const String& string)
 {
-    out.print(string.utf8());
+    printExpectedCStringHelper(out, "String", string.tryGetUtf8());
 }
 
 void printInternal(PrintStream& out, const StringImpl* string)
@@ -103,7 +115,7 @@ void printInternal(PrintStream& out, const StringImpl* string)
         out.print("(null StringImpl*)");
         return;
     }
-    out.print(string->utf8());
+    printExpectedCStringHelper(out, "StringImpl*", string->tryGetUtf8());
 }
 
 void printInternal(PrintStream& out, bool value)
index dfe1d2c..d50c3a3 100644 (file)
@@ -1726,7 +1726,7 @@ static inline void putUTF8Triple(char*& buffer, UChar character)
     *buffer++ = static_cast<char>((character & 0x3F) | 0x80);
 }
 
-bool StringImpl::utf8Impl(const UChar* characters, unsigned length, char*& buffer, size_t bufferSize, ConversionMode mode)
+UTF8ConversionError StringImpl::utf8Impl(const UChar* characters, unsigned length, char*& buffer, size_t bufferSize, ConversionMode mode)
 {
     if (mode == StrictConversionReplacingUnpairedSurrogatesWithFFFD) {
         const UChar* charactersEnd = characters + length;
@@ -1754,13 +1754,13 @@ bool StringImpl::utf8Impl(const UChar* characters, unsigned length, char*& buffe
         // Only produced from strict conversion.
         if (result == sourceIllegal) {
             ASSERT(strict);
-            return false;
+            return UTF8ConversionError::IllegalSource;
         }
 
         // Check for an unconverted high surrogate.
         if (result == sourceExhausted) {
             if (strict)
-                return false;
+                return UTF8ConversionError::SourceExhausted;
             // This should be one unpaired high surrogate. Treat it the same
             // was as an unpaired high surrogate would have been handled in
             // the middle of a string with non-strict conversion - which is
@@ -1774,15 +1774,15 @@ bool StringImpl::utf8Impl(const UChar* characters, unsigned length, char*& buffe
         }
     }
     
-    return true;
+    return UTF8ConversionError::None;
 }
 
-CString StringImpl::utf8ForCharacters(const LChar* characters, unsigned length)
+Expected<CString, UTF8ConversionError> StringImpl::utf8ForCharacters(const LChar* characters, unsigned length)
 {
     if (!length)
         return CString("", 0);
     if (length > std::numeric_limits<unsigned>::max() / 3)
-        return CString();
+        return makeUnexpected(UTF8ConversionError::OutOfMemory);
     Vector<char, 1024> bufferVector(length * 3);
     char* buffer = bufferVector.data();
     const LChar* source = characters;
@@ -1791,20 +1791,21 @@ CString StringImpl::utf8ForCharacters(const LChar* characters, unsigned length)
     return CString(bufferVector.data(), buffer - bufferVector.data());
 }
 
-CString StringImpl::utf8ForCharacters(const UChar* characters, unsigned length, ConversionMode mode)
+Expected<CString, UTF8ConversionError> StringImpl::utf8ForCharacters(const UChar* characters, unsigned length, ConversionMode mode)
 {
     if (!length)
         return CString("", 0);
     if (length > std::numeric_limits<unsigned>::max() / 3)
-        return CString();
+        return makeUnexpected(UTF8ConversionError::OutOfMemory);
     Vector<char, 1024> bufferVector(length * 3);
     char* buffer = bufferVector.data();
-    if (!utf8Impl(characters, length, buffer, bufferVector.size(), mode))
-        return CString();
+    UTF8ConversionError error = utf8Impl(characters, length, buffer, bufferVector.size(), mode);
+    if (error != UTF8ConversionError::None)
+        return makeUnexpected(error);
     return CString(bufferVector.data(), buffer - bufferVector.data());
 }
 
-CString StringImpl::utf8ForRange(unsigned offset, unsigned length, ConversionMode mode) const
+Expected<CString, UTF8ConversionError> StringImpl::tryGetUtf8ForRange(unsigned offset, unsigned length, ConversionMode mode) const
 {
     ASSERT(offset <= this->length());
     ASSERT(offset + length <= this->length());
@@ -1823,7 +1824,7 @@ CString StringImpl::utf8ForRange(unsigned offset, unsigned length, ConversionMod
     //    have a good chance of being able to write the string into the
     //    buffer without reallocing (say, 1.5 x length).
     if (length > std::numeric_limits<unsigned>::max() / 3)
-        return CString();
+        return makeUnexpected(UTF8ConversionError::OutOfMemory);
     Vector<char, 1024> bufferVector(length * 3);
 
     char* buffer = bufferVector.data();
@@ -1834,16 +1835,24 @@ CString StringImpl::utf8ForRange(unsigned offset, unsigned length, ConversionMod
         ConversionResult result = convertLatin1ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size());
         ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be sufficient for any conversion
     } else {
-        if (!utf8Impl(this->characters16() + offset, length, buffer, bufferVector.size(), mode))
-            return CString();
+        UTF8ConversionError error = utf8Impl(this->characters16() + offset, length, buffer, bufferVector.size(), mode);
+        if (error != UTF8ConversionError::None)
+            return makeUnexpected(error);
     }
 
     return CString(bufferVector.data(), buffer - bufferVector.data());
 }
 
+Expected<CString, UTF8ConversionError> StringImpl::tryGetUtf8(ConversionMode mode) const
+{
+    return tryGetUtf8ForRange(0, length(), mode);
+}
+
 CString StringImpl::utf8(ConversionMode mode) const
 {
-    return utf8ForRange(0, length(), mode);
+    auto expectedString = tryGetUtf8ForRange(0, length(), mode);
+    RELEASE_ASSERT(expectedString);
+    return expectedString.value();
 }
 
 NEVER_INLINE unsigned StringImpl::hashSlowCase() const
index af4ce6b..2aa0c91 100644 (file)
@@ -27,6 +27,7 @@
 #include <unicode/ustring.h>
 #include <wtf/ASCIICType.h>
 #include <wtf/CheckedArithmetic.h>
+#include <wtf/Expected.h>
 #include <wtf/MathExtras.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/Vector.h>
@@ -34,6 +35,7 @@
 #include <wtf/text/ConversionMode.h>
 #include <wtf/text/StringCommon.h>
 #include <wtf/text/StringHasher.h>
+#include <wtf/text/UTF8ConversionError.h>
 
 #if USE(CF)
 typedef const struct __CFString * CFStringRef;
@@ -284,13 +286,15 @@ public:
     bool isSubString() const { return bufferOwnership() == BufferSubstring; }
 #endif
 
-    static WTF_EXPORT_PRIVATE CString utf8ForCharacters(const LChar* characters, unsigned length);
-    static WTF_EXPORT_PRIVATE CString utf8ForCharacters(const UChar* characters, unsigned length, ConversionMode = LenientConversion);
-    WTF_EXPORT_PRIVATE CString utf8ForRange(unsigned offset, unsigned length, ConversionMode = LenientConversion) const;
+    static WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> utf8ForCharacters(const LChar* characters, unsigned length);
+    static WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> utf8ForCharacters(const UChar* characters, unsigned length, ConversionMode = LenientConversion);
+
+    WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> tryGetUtf8ForRange(unsigned offset, unsigned length, ConversionMode = LenientConversion) const;
+    WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> tryGetUtf8(ConversionMode = LenientConversion) const;
     WTF_EXPORT_PRIVATE CString utf8(ConversionMode = LenientConversion) const;
 
 private:
-    static WTF_EXPORT_PRIVATE bool utf8Impl(const UChar* characters, unsigned length, char*& buffer, size_t bufferSize, ConversionMode);
+    static WTF_EXPORT_PRIVATE UTF8ConversionError utf8Impl(const UChar* characters, unsigned length, char*& buffer, size_t bufferSize, ConversionMode);
     
     // The high bits of 'hash' are always empty, but we prefer to store our flags
     // in the low bits because it makes them slightly more efficient to access.
@@ -1202,8 +1206,8 @@ template<unsigned length> inline bool equalLettersIgnoringASCIICase(const String
 
 } // namespace WTF
 
-using WTF::StringImpl;
 using WTF::StaticStringImpl;
+using WTF::StringImpl;
 using WTF::equal;
 
 #endif
index ec10919..37898f5 100644 (file)
@@ -80,7 +80,7 @@ bool StringView::endsWithIgnoringASCIICase(const StringView& suffix) const
     return ::WTF::endsWithIgnoringASCIICase(*this, suffix);
 }
 
-CString StringView::utf8(ConversionMode mode) const
+Expected<CString, UTF8ConversionError> StringView::tryGetUtf8(ConversionMode mode) const
 {
     if (isNull())
         return CString("", 0);
@@ -89,6 +89,13 @@ CString StringView::utf8(ConversionMode mode) const
     return StringImpl::utf8ForCharacters(characters16(), length(), mode);
 }
 
+CString StringView::utf8(ConversionMode mode) const
+{
+    auto expectedString = tryGetUtf8(mode);
+    RELEASE_ASSERT(expectedString);
+    return expectedString.value();
+}
+
 size_t StringView::find(StringView matchString, unsigned start) const
 {
     return findCommon(*this, matchString, start);
index 822c65a..ce9822b 100644 (file)
@@ -36,6 +36,7 @@
 #include <wtf/text/ConversionMode.h>
 #include <wtf/text/LChar.h>
 #include <wtf/text/StringCommon.h>
+#include <wtf/text/UTF8ConversionError.h>
 
 // FIXME: Enabling the StringView lifetime checking causes the MSVC build to fail. Figure out why.
 #if defined(NDEBUG) || COMPILER(MSVC)
@@ -106,6 +107,7 @@ public:
     WTF_EXPORT_PRIVATE RetainPtr<NSString> createNSStringWithoutCopying() const;
 #endif
 
+    WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> tryGetUtf8(ConversionMode = LenientConversion) const;
     WTF_EXPORT_PRIVATE CString utf8(ConversionMode = LenientConversion) const;
 
     class UpconvertedCharacters;
diff --git a/Source/WTF/wtf/text/UTF8ConversionError.h b/Source/WTF/wtf/text/UTF8ConversionError.h
new file mode 100644 (file)
index 0000000..8fb28ee
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WTF {
+
+enum class UTF8ConversionError {
+    None,
+    OutOfMemory,
+    IllegalSource,
+    SourceExhausted
+};
+
+} // namespace WTF
+
+using WTF::UTF8ConversionError;
index 5fe7521..b24c79f 100644 (file)
@@ -804,9 +804,21 @@ CString String::latin1() const
     return result;
 }
 
+Expected<CString, UTF8ConversionError> String::tryGetUtf8(ConversionMode mode) const
+{
+    return m_impl ? m_impl->tryGetUtf8(mode) : CString { "", 0 };
+}
+
+Expected<CString, UTF8ConversionError> String::tryGetUtf8() const
+{
+    return tryGetUtf8(LenientConversion);
+}
+
 CString String::utf8(ConversionMode mode) const
 {
-    return m_impl ? m_impl->utf8(mode) : CString { "", 0 };
+    Expected<CString, UTF8ConversionError> expectedString = tryGetUtf8(mode);
+    RELEASE_ASSERT(expectedString);
+    return expectedString.value();
 }
 
 CString String::utf8() const
index a3e91d9..17e2c69 100644 (file)
@@ -165,6 +165,9 @@ public:
     WTF_EXPORT_PRIVATE CString utf8(ConversionMode) const;
     WTF_EXPORT_PRIVATE CString utf8() const;
 
+    WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> tryGetUtf8(ConversionMode) const;
+    WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> tryGetUtf8() const;
+
     UChar characterAt(unsigned index) const;
     UChar operator[](unsigned index) const { return characterAt(index); }