Add support for checked arithmetic
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 31 Aug 2011 18:16:11 +0000 (18:16 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 31 Aug 2011 18:16:11 +0000 (18:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=67095

Reviewed by Sam Weinig.

../../../../Volumes/Data/git/WebKit/OpenSource/Source/JavaScriptCore:

Add a checked arithmetic class Checked<T> that provides overflow-safe
arithmetic over all integral types.  Checked<T> supports addition, subtraction
and multiplication, along with "bool" conversions and equality operators.

Checked<> can be used in either CRASH() on overflow or delayed failure modes,
although the default is to CRASH().

To ensure the code is actually in use (rather than checking in dead code) I've
made a couple of properties in YARR use Checked<int> and Checked<unsigned>
instead of raw value arithmetic.  This has resulted in a moderate set of changes,
to YARR - mostly adding .get() calls, but a couple of casts from unsigned long
to unsigned for some uses of sizeof, as Checked<> currently does not support
mixed signed-ness of types wider that 32 bits.

Happily the increased type safety of Checked<> means that it's not possible to
accidentally assign away precision, nor accidentally call integer overload of
a function instead of the bool version.

No measurable regression in performance, and SunSpider claims this patch to be
a progression of 0.3%.

* GNUmakefile.list.am:
* JavaScriptCore.gypi:
* JavaScriptCore.vcproj/WTF/WTF.vcproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* wtf/CheckedArithmetic.h: Added.
(WTF::CrashOnOverflow::overflowed):
(WTF::CrashOnOverflow::clearOverflow):
(WTF::CrashOnOverflow::hasOverflowed):
(WTF::RecordOverflow::RecordOverflow):
(WTF::RecordOverflow::overflowed):
(WTF::RecordOverflow::clearOverflow):
(WTF::RecordOverflow::hasOverflowed):
(WTF::isInBounds):
(WTF::safeAdd):
(WTF::safeSub):
(WTF::safeMultiply):
(WTF::safeEquals):
(WTF::workAroundClangBug):
(WTF::Checked::Checked):
(WTF::Checked::operator=):
(WTF::Checked::operator++):
(WTF::Checked::operator--):
(WTF::Checked::operator!):
(WTF::Checked::operator UnspecifiedBoolType*):
(WTF::Checked::get):
(WTF::Checked::operator+=):
(WTF::Checked::operator-=):
(WTF::Checked::operator*=):
(WTF::Checked::operator==):
(WTF::Checked::operator!=):
(WTF::operator+):
(WTF::operator-):
(WTF::operator*):
* yarr/YarrInterpreter.cpp:
(JSC::Yarr::ByteCompiler::atomPatternCharacter):
(JSC::Yarr::ByteCompiler::atomCharacterClass):
(JSC::Yarr::ByteCompiler::atomBackReference):
(JSC::Yarr::ByteCompiler::atomParentheticalAssertionEnd):
(JSC::Yarr::ByteCompiler::atomParenthesesSubpatternEnd):
(JSC::Yarr::ByteCompiler::atomParenthesesOnceEnd):
(JSC::Yarr::ByteCompiler::atomParenthesesTerminalEnd):
* yarr/YarrInterpreter.h:
(JSC::Yarr::ByteTerm::ByteTerm):
(JSC::Yarr::ByteTerm::CheckInput):
(JSC::Yarr::ByteTerm::UncheckInput):
* yarr/YarrJIT.cpp:
(JSC::Yarr::YarrGenerator::generateAssertionEOL):
(JSC::Yarr::YarrGenerator::generatePatternCharacterFixed):
(JSC::Yarr::YarrGenerator::generatePatternCharacterGreedy):
(JSC::Yarr::YarrGenerator::backtrackPatternCharacterNonGreedy):
(JSC::Yarr::YarrGenerator::generateCharacterClassOnce):
(JSC::Yarr::YarrGenerator::generateCharacterClassFixed):
(JSC::Yarr::YarrGenerator::generateCharacterClassGreedy):
(JSC::Yarr::YarrGenerator::backtrackCharacterClassNonGreedy):
* yarr/YarrPattern.cpp:
(JSC::Yarr::YarrPatternConstructor::setupAlternativeOffsets):
* yarr/YarrPattern.h:

../../../../Volumes/Data/git/WebKit/OpenSource/Source/WebCore:

Add a forwarding header for CheckedArithmetic.h

* ForwardingHeaders/wtf/CheckedArithmetic.h: Added.

../../../../Volumes/Data/git/WebKit/OpenSource/Tools:

Add test cases for Checked<>

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/CheckedArithmeticOperations.cpp: Added.

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

16 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GNUmakefile.list.am
Source/JavaScriptCore/JavaScriptCore.gypi
Source/JavaScriptCore/JavaScriptCore.vcproj/WTF/WTF.vcproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/wtf/CheckedArithmetic.h [new file with mode: 0644]
Source/JavaScriptCore/yarr/YarrInterpreter.cpp
Source/JavaScriptCore/yarr/YarrInterpreter.h
Source/JavaScriptCore/yarr/YarrJIT.cpp
Source/JavaScriptCore/yarr/YarrPattern.cpp
Source/JavaScriptCore/yarr/YarrPattern.h
Source/WebCore/ChangeLog
Source/WebCore/ForwardingHeaders/wtf/CheckedArithmetic.h [new file with mode: 0644]
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/CheckedArithmeticOperations.cpp [new file with mode: 0644]

index 1c0e3e2..d92934f 100644 (file)
@@ -1,3 +1,89 @@
+2011-08-30  Oliver Hunt  <oliver@apple.com>
+
+        Add support for checked arithmetic
+        https://bugs.webkit.org/show_bug.cgi?id=67095
+
+        Reviewed by Sam Weinig.
+
+        Add a checked arithmetic class Checked<T> that provides overflow-safe
+        arithmetic over all integral types.  Checked<T> supports addition, subtraction
+        and multiplication, along with "bool" conversions and equality operators.
+
+        Checked<> can be used in either CRASH() on overflow or delayed failure modes,
+        although the default is to CRASH().
+
+        To ensure the code is actually in use (rather than checking in dead code) I've
+        made a couple of properties in YARR use Checked<int> and Checked<unsigned>
+        instead of raw value arithmetic.  This has resulted in a moderate set of changes,
+        to YARR - mostly adding .get() calls, but a couple of casts from unsigned long
+        to unsigned for some uses of sizeof, as Checked<> currently does not support
+        mixed signed-ness of types wider that 32 bits.
+
+        Happily the increased type safety of Checked<> means that it's not possible to
+        accidentally assign away precision, nor accidentally call integer overload of
+        a function instead of the bool version.
+
+        No measurable regression in performance, and SunSpider claims this patch to be
+        a progression of 0.3%.
+
+        * GNUmakefile.list.am:
+        * JavaScriptCore.gypi:
+        * JavaScriptCore.vcproj/WTF/WTF.vcproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * wtf/CheckedArithmetic.h: Added.
+        (WTF::CrashOnOverflow::overflowed):
+        (WTF::CrashOnOverflow::clearOverflow):
+        (WTF::CrashOnOverflow::hasOverflowed):
+        (WTF::RecordOverflow::RecordOverflow):
+        (WTF::RecordOverflow::overflowed):
+        (WTF::RecordOverflow::clearOverflow):
+        (WTF::RecordOverflow::hasOverflowed):
+        (WTF::isInBounds):
+        (WTF::safeAdd):
+        (WTF::safeSub):
+        (WTF::safeMultiply):
+        (WTF::safeEquals):
+        (WTF::workAroundClangBug):
+        (WTF::Checked::Checked):
+        (WTF::Checked::operator=):
+        (WTF::Checked::operator++):
+        (WTF::Checked::operator--):
+        (WTF::Checked::operator!):
+        (WTF::Checked::operator UnspecifiedBoolType*):
+        (WTF::Checked::get):
+        (WTF::Checked::operator+=):
+        (WTF::Checked::operator-=):
+        (WTF::Checked::operator*=):
+        (WTF::Checked::operator==):
+        (WTF::Checked::operator!=):
+        (WTF::operator+):
+        (WTF::operator-):
+        (WTF::operator*):
+        * yarr/YarrInterpreter.cpp:
+        (JSC::Yarr::ByteCompiler::atomPatternCharacter):
+        (JSC::Yarr::ByteCompiler::atomCharacterClass):
+        (JSC::Yarr::ByteCompiler::atomBackReference):
+        (JSC::Yarr::ByteCompiler::atomParentheticalAssertionEnd):
+        (JSC::Yarr::ByteCompiler::atomParenthesesSubpatternEnd):
+        (JSC::Yarr::ByteCompiler::atomParenthesesOnceEnd):
+        (JSC::Yarr::ByteCompiler::atomParenthesesTerminalEnd):
+        * yarr/YarrInterpreter.h:
+        (JSC::Yarr::ByteTerm::ByteTerm):
+        (JSC::Yarr::ByteTerm::CheckInput):
+        (JSC::Yarr::ByteTerm::UncheckInput):
+        * yarr/YarrJIT.cpp:
+        (JSC::Yarr::YarrGenerator::generateAssertionEOL):
+        (JSC::Yarr::YarrGenerator::generatePatternCharacterFixed):
+        (JSC::Yarr::YarrGenerator::generatePatternCharacterGreedy):
+        (JSC::Yarr::YarrGenerator::backtrackPatternCharacterNonGreedy):
+        (JSC::Yarr::YarrGenerator::generateCharacterClassOnce):
+        (JSC::Yarr::YarrGenerator::generateCharacterClassFixed):
+        (JSC::Yarr::YarrGenerator::generateCharacterClassGreedy):
+        (JSC::Yarr::YarrGenerator::backtrackCharacterClassNonGreedy):
+        * yarr/YarrPattern.cpp:
+        (JSC::Yarr::YarrPatternConstructor::setupAlternativeOffsets):
+        * yarr/YarrPattern.h:
+
 2011-08-31  Andrei Popescu  <andreip@google.com>
 
         Investigate current uses of OS(ANDROID)
index c0c6b70..6d2e27d 100644 (file)
@@ -459,6 +459,7 @@ javascriptcore_sources += \
        Source/JavaScriptCore/wtf/BumpPointerAllocator.h \
        Source/JavaScriptCore/wtf/ByteArray.cpp \
        Source/JavaScriptCore/wtf/ByteArray.h \
+       Source/JavaScriptCore/wtf/CheckedArithmetic.h \
        Source/JavaScriptCore/wtf/Compiler.h \
        Source/JavaScriptCore/wtf/CrossThreadRefCounted.h \
        Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.cpp \
index 6120492..2807132 100644 (file)
             'wtf/BloomFilter.h',
             'wtf/BumpPointerAllocator.h',
             'wtf/ByteArray.h',
+            'wtf/CheckedArithmetic.h',
             'wtf/Compiler.h',
             'wtf/Complex.h',
             'wtf/CrossThreadRefCounted.h',
index 348c26f..a388988 100644 (file)
                        >
                </File>
                <File
+                       RelativePath="..\..\wtf\CheckedArithmetic.h"
+                       >
+               </File>
+               <File
                        RelativePath="..\..\wtf\Compiler.h"
                        >
                </File>
index cb667d8..7537f5c 100644 (file)
                A7A1F7AC0F252B3C00E184E2 /* ByteArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7A1F7AA0F252B3C00E184E2 /* ByteArray.cpp */; };
                A7A1F7AD0F252B3C00E184E2 /* ByteArray.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A1F7AB0F252B3C00E184E2 /* ByteArray.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A7B48F490EE8936F00DCBDB6 /* ExecutableAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7B48DB60EE74CFC00DCBDB6 /* ExecutableAllocator.cpp */; };
+               A7BC0C82140608B000B1BB71 /* CheckedArithmetic.h in Headers */ = {isa = PBXBuildFile; fileRef = A7BC0C81140608B000B1BB71 /* CheckedArithmetic.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A7C1E8E4112E72EF00A37F98 /* JITPropertyAccess32_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7C1E8C8112E701C00A37F98 /* JITPropertyAccess32_64.cpp */; };
                A7C40C0A130B057D00D002A1 /* BlockStack.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C40C07130B057D00D002A1 /* BlockStack.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A7C40C0B130B057D00D002A1 /* SentinelLinkedList.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C40C08130B057D00D002A1 /* SentinelLinkedList.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A7A7EE7711B98B8D0065A14F /* SyntaxChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyntaxChecker.h; sourceTree = "<group>"; };
                A7B48DB50EE74CFC00DCBDB6 /* ExecutableAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutableAllocator.h; sourceTree = "<group>"; };
                A7B48DB60EE74CFC00DCBDB6 /* ExecutableAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocator.cpp; sourceTree = "<group>"; };
+               A7BC0C81140608B000B1BB71 /* CheckedArithmetic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CheckedArithmetic.h; sourceTree = "<group>"; };
                A7C1E8C8112E701C00A37F98 /* JITPropertyAccess32_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITPropertyAccess32_64.cpp; sourceTree = "<group>"; };
                A7C225CC139981F100FF1662 /* KeywordLookupGenerator.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = KeywordLookupGenerator.py; sourceTree = "<group>"; };
                A7C225CD1399849C00FF1662 /* KeywordLookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeywordLookup.h; sourceTree = "<group>"; };
                                86676D4D11FED55D004B6863 /* BumpPointerAllocator.h */,
                                A7A1F7AA0F252B3C00E184E2 /* ByteArray.cpp */,
                                A7A1F7AB0F252B3C00E184E2 /* ByteArray.h */,
+                               A7BC0C81140608B000B1BB71 /* CheckedArithmetic.h */,
                                BC66BAE213F4928F00C23FAE /* Compiler.h */,
                                FDA15C1612B03028003A583A /* Complex.h */,
                                0BDFFAD40FC6171000D69EF4 /* CrossThreadRefCounted.h */,
                                866739D213BFDE710023D87C /* BigInteger.h in Headers */,
                                866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */,
                                BC66BAE413F492CC00C23FAE /* Compiler.h in Headers */,
+                               A7BC0C82140608B000B1BB71 /* CheckedArithmetic.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/Source/JavaScriptCore/wtf/CheckedArithmetic.h b/Source/JavaScriptCore/wtf/CheckedArithmetic.h
new file mode 100644 (file)
index 0000000..d972a7f
--- /dev/null
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2011 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. 
+ */
+
+#ifndef CheckedArithmetic_h
+#define CheckedArithmetic_h
+
+#include "Assertions.h"
+#include "TypeTraits.h"
+
+#include <limits>
+
+/* Checked<T>
+ *
+ * This class provides a mechanism to perform overflow-safe integer arithmetic
+ * without having to manually ensure that you have all the required bounds checks
+ * directly in your code.
+ *
+ * There are two modes of operation:
+ *  - The default is Checked<T, CrashOnOverflow>, and crashes at the point
+ *    and overflow has occurred.
+ *  - The alternative is Checked<T, RecordOverflow>, which uses an additional
+ *    byte of storage to track whether an overflow has occurred, subsequent
+ *    unchecked operations will crash if an overflow has occured
+ *
+ * It is possible to provide a custom overflow handler, in which case you need
+ * to support these functions:
+ *  - void overflowed();
+ *    This function is called when an operation has produced an overflow.
+ *  - bool hasOverflowed();
+ *    This function must return true if overflowed() has been called on an
+ *    instance and false if it has not.
+ *  - void clearOverflow();
+ *    Used to reset overflow tracking when a value is being overwritten with
+ *    a new value.
+ *
+ * Checked<T> works for all integer types, with the following caveats:
+ *  - Mixing signedness of operands is only supported for types narrower than
+ *    64bits.
+ *  - It does have a performance impact, so tight loops may want to be careful
+ *    when using it.
+ *
+ */
+
+namespace WTF {
+
+class CrashOnOverflow {
+protected:
+    NO_RETURN_DUE_TO_CRASH void overflowed()
+    {
+        CRASH();
+    }
+
+    void clearOverflow() { }
+
+public:
+    bool hasOverflowed() const { return false; }
+};
+
+class RecordOverflow {
+protected:
+    RecordOverflow()
+        : m_overflowed(false)
+    {
+    }
+
+    void overflowed()
+    {
+        m_overflowed = true;
+    }
+
+    void clearOverflow()
+    {
+        m_overflowed = false;
+    }
+
+public:
+    bool hasOverflowed() const { return m_overflowed; }
+
+private:
+    unsigned char m_overflowed;
+};
+
+template <typename T, class OverflowHandler = CrashOnOverflow> class Checked;
+template <typename T> struct RemoveChecked;
+template <typename T> struct RemoveChecked<Checked<T> >;
+
+template <typename Target, typename Source, bool targetSigned = std::numeric_limits<Target>::is_signed, bool sourceSigned = std::numeric_limits<Source>::is_signed> struct BoundsChecker;
+template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, false> {
+    static bool inBounds(Source value)
+    {
+        // Same signedness so implicit type conversion will always increase precision
+        // to widest type
+        return value <= std::numeric_limits<Target>::max();
+    }
+};
+
+template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, true> {
+    static bool inBounds(Source value)
+    {
+        // Same signedness so implicit type conversion will always increase precision
+        // to widest type
+        return std::numeric_limits<Target>::min() <= value && value <= std::numeric_limits<Target>::max();
+    }
+};
+
+template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, true> {
+    static bool inBounds(Source value)
+    {
+        // Target is unsigned so any value less than zero is clearly unsafe
+        if (value < 0)
+            return false;
+        // If our (unsigned) Target is the same or greater width we can
+        // convert value to type Target without losing precision
+        if (sizeof(Target) >= sizeof(Source)) 
+            return static_cast<Target>(value) <= std::numeric_limits<Target>::max();
+        // The signed Source type has greater precision than the target so
+        // max(Target) -> Source will widen.
+        return value <= static_cast<Source>(std::numeric_limits<Target>::max());
+    }
+};
+
+template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, false> {
+    static bool inBounds(Source value)
+    {
+        // Signed target with an unsigned source
+        if (sizeof(Target) <= sizeof(Source)) 
+            return value <= static_cast<Source>(std::numeric_limits<Target>::max());
+        // Target is Wider than Source so we're guaranteed to fit any value in
+        // unsigned Source
+        return true;
+    }
+};
+
+template <typename Target, typename Source, bool SameType = IsSameType<Target, Source>::value> struct BoundsCheckElider;
+template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, true> {
+    static bool inBounds(Source) { return true; }
+};
+template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, false> : public BoundsChecker<Target, Source> {
+};
+
+template <typename Target, typename Source> static inline bool isInBounds(Source value)
+{
+    return BoundsCheckElider<Target, Source>::inBounds(value);
+}
+
+template <typename T> struct RemoveChecked {
+    typedef T CleanType;
+    static const CleanType DefaultValue = 0;    
+};
+
+template <typename T> struct RemoveChecked<Checked<T, CrashOnOverflow> > {
+    typedef typename RemoveChecked<T>::CleanType CleanType;
+    static const CleanType DefaultValue = 0;
+};
+
+template <typename T> struct RemoveChecked<Checked<T, RecordOverflow> > {
+    typedef typename RemoveChecked<T>::CleanType CleanType;
+    static const CleanType DefaultValue = 0;
+};
+
+// The ResultBase and SignednessSelector are used to workaround typeof not being
+// available in MSVC
+template <typename U, typename V, bool uIsBigger = (sizeof(U) > sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase;
+template <typename U, typename V> struct ResultBase<U, V, true, false> {
+    typedef U ResultType;
+};
+
+template <typename U, typename V> struct ResultBase<U, V, false, false> {
+    typedef V ResultType;
+};
+
+template <typename U> struct ResultBase<U, U, false, true> {
+    typedef U ResultType;
+};
+
+template <typename U, typename V, bool uIsSigned = std::numeric_limits<U>::is_signed, bool vIsSigned = std::numeric_limits<V>::is_signed> struct SignednessSelector;
+template <typename U, typename V> struct SignednessSelector<U, V, true, true> {
+    typedef U ResultType;
+};
+
+template <typename U, typename V> struct SignednessSelector<U, V, false, false> {
+    typedef U ResultType;
+};
+
+template <typename U, typename V> struct SignednessSelector<U, V, true, false> {
+    typedef V ResultType;
+};
+
+template <typename U, typename V> struct SignednessSelector<U, V, false, true> {
+    typedef U ResultType;
+};
+
+template <typename U, typename V> struct ResultBase<U, V, false, true> {
+    typedef typename SignednessSelector<U, V>::ResultType ResultType;
+};
+
+template <typename U, typename V> struct Result : ResultBase<typename RemoveChecked<U>::CleanType, typename RemoveChecked<V>::CleanType> {
+};
+
+template <typename LHS, typename RHS, typename ResultType = typename Result<LHS, RHS>::ResultType, 
+    bool lhsSigned = std::numeric_limits<LHS>::is_signed, bool rhsSigned = std::numeric_limits<RHS>::is_signed> struct ArithmeticOperations;
+
+template <typename LHS, typename RHS, typename ResultType> struct ArithmeticOperations<LHS, RHS, ResultType, true, true> {
+    // LHS and RHS are signed types
+
+    // Helper function
+    static inline bool signsMatch(LHS lhs, RHS rhs)
+    {
+        return (lhs ^ rhs) >= 0;
+    }
+
+    static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN
+    {
+        if (signsMatch(lhs, rhs)) {
+            if (lhs >= 0) {
+                if ((std::numeric_limits<ResultType>::max() - rhs) < lhs)
+                    return false;
+            } else {
+                ResultType temp = lhs - std::numeric_limits<ResultType>::min();
+                if (rhs < -temp)
+                    return false;
+            }
+        } // if the signs do not match this operation can't overflow
+        result = lhs + rhs;
+        return true;
+    }
+
+    static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN
+    {
+        if (!signsMatch(lhs, rhs)) {
+            if (lhs >= 0) {
+                if (lhs > std::numeric_limits<ResultType>::max() + rhs)
+                    return false;
+            } else {
+                if (rhs > std::numeric_limits<ResultType>::max() + lhs)
+                    return false;
+            }
+        } // if the signs match this operation can't overflow
+        result = lhs - rhs;
+        return true;
+    }
+
+    static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN
+    {
+        if (signsMatch(lhs, rhs)) {
+            if (lhs >= 0) {
+                if (lhs && (std::numeric_limits<ResultType>::max() / lhs) < rhs)
+                    return false;
+            } else {
+                if (lhs == std::numeric_limits<ResultType>::min() || rhs == std::numeric_limits<ResultType>::min())
+                    return false;
+                if ((std::numeric_limits<ResultType>::max() / -lhs) < -rhs)
+                    return false;
+            }
+        } else {
+            if (lhs < 0) {
+                if (rhs && lhs < (std::numeric_limits<ResultType>::min() / rhs))
+                    return false;
+            } else {
+                if (lhs && rhs < (std::numeric_limits<ResultType>::min() / lhs))
+                    return false;
+            }
+        }
+        result = lhs * rhs;
+        return true;
+    }
+
+    static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; }
+
+};
+
+template <typename LHS, typename RHS, typename ResultType> struct ArithmeticOperations<LHS, RHS, ResultType, false, false> {
+    // LHS and RHS are unsigned types so bounds checks are nice and easy
+    static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN
+    {
+        ResultType temp = lhs + rhs;
+        if (temp < lhs)
+            return false;
+        result = temp;
+        return true;
+    }
+
+    static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN
+    {
+        ResultType temp = lhs - rhs;
+        if (temp > lhs)
+            return false;
+        result = temp;
+        return true;
+    }
+
+    static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN
+    {
+        ResultType temp = lhs * rhs;
+        if (temp < lhs)
+            return false;
+        result = temp;
+        return true;
+    }
+
+    static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; }
+
+};
+
+template <typename ResultType> struct ArithmeticOperations<int, unsigned, ResultType, true, false> {
+    static inline bool add(int64_t lhs, int64_t rhs, ResultType& result)
+    {
+        int64_t temp = lhs + rhs;
+        if (temp < std::numeric_limits<ResultType>::min())
+            return false;
+        if (temp > std::numeric_limits<ResultType>::max())
+            return false;
+        result = static_cast<ResultType>(temp);
+        return true;
+    }
+    
+    static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result)
+    {
+        int64_t temp = lhs - rhs;
+        if (temp < std::numeric_limits<ResultType>::min())
+            return false;
+        if (temp > std::numeric_limits<ResultType>::max())
+            return false;
+        result = static_cast<ResultType>(temp);
+        return true;
+    }
+
+    static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result)
+    {
+        int64_t temp = lhs * rhs;
+        if (temp < std::numeric_limits<ResultType>::min())
+            return false;
+        if (temp > std::numeric_limits<ResultType>::max())
+            return false;
+        result = static_cast<ResultType>(temp);
+        return true;
+    }
+
+    static inline bool equals(int lhs, unsigned rhs)
+    {
+        return static_cast<int64_t>(lhs) == static_cast<int64_t>(rhs);
+    }
+};
+
+template <typename ResultType> struct ArithmeticOperations<unsigned, int, ResultType, false, true> {
+    static inline bool add(int64_t lhs, int64_t rhs, ResultType& result)
+    {
+        return ArithmeticOperations<int, unsigned, ResultType>::add(rhs, lhs, result);
+    }
+    
+    static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result)
+    {
+        return ArithmeticOperations<int, unsigned, ResultType>::sub(lhs, rhs, result);
+    }
+
+    static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result)
+    {
+        return ArithmeticOperations<int, unsigned, ResultType>::multiply(rhs, lhs, result);
+    }
+
+    static inline bool equals(unsigned lhs, int rhs)
+    {
+        return ArithmeticOperations<int, unsigned, ResultType>::equals(rhs, lhs);
+    }
+};
+
+template <typename U, typename V, typename R> static inline bool safeAdd(U lhs, V rhs, R& result)
+{
+    return ArithmeticOperations<U, V, R>::add(lhs, rhs, result);
+}
+
+template <typename U, typename V, typename R> static inline bool safeSub(U lhs, V rhs, R& result)
+{
+    return ArithmeticOperations<U, V, R>::sub(lhs, rhs, result);
+}
+
+template <typename U, typename V, typename R> static inline bool safeMultiply(U lhs, V rhs, R& result)
+{
+    return ArithmeticOperations<U, V, R>::multiply(lhs, rhs, result);
+}
+
+template <typename U, typename V> static inline bool safeEquals(U lhs, V rhs)
+{
+    return ArithmeticOperations<U, V>::equals(lhs, rhs);
+}
+
+enum ResultOverflowedTag { ResultOverflowed };
+    
+// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801
+static inline bool workAroundClangBug() { return true; }
+
+template <typename T, class OverflowHandler> class Checked : public OverflowHandler {
+public:
+    template <typename _T, class _OverflowHandler> friend class Checked;
+    Checked()
+        : m_value(0)
+    {
+    }
+
+    Checked(ResultOverflowedTag)
+        : m_value(0)
+    {
+        // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801
+        if (workAroundClangBug())
+            this->overflowed();
+    }
+
+    template <typename U> Checked(U value)
+    {
+        if (!isInBounds<T>(value))
+            this->overflowed();
+        m_value = static_cast<T>(value);
+    }
+    
+    template <typename V> Checked(const Checked<T, V>& rhs)
+        : m_value(rhs.m_value)
+    {
+        if (rhs.hasOverflowed())
+            this->overflowed();
+    }
+    
+    template <typename U> Checked(const Checked<U, OverflowHandler>& rhs)
+        : OverflowHandler(rhs)
+    {
+        if (!isInBounds<T>(rhs.m_value))
+            this->overflowed();
+        m_value = rhs.m_value;
+    }
+    
+    template <typename U, typename V> Checked(const Checked<U, V>& rhs)
+    {
+        if (rhs.hasOverflowed())
+            this->overflowed();
+        if (!isInBounds<T>(rhs.m_value))
+            this->overflowed();
+        m_value = rhs.m_value;
+    }
+    
+    const Checked& operator=(Checked rhs)
+    {
+        this->clearOverflow();
+        if (rhs.hasOverflowed())
+            this->overflowed();
+        m_value = rhs.m_value;
+        return *this;
+    }
+    
+    template <typename U> const Checked& operator=(U value)
+    {
+        return *this = Checked(value);
+    }
+    
+    template <typename U, typename V> const Checked& operator=(const Checked<U, V>& rhs)
+    {
+        return *this = Checked(rhs);
+    }
+    
+    // prefix
+    const Checked& operator++()
+    {
+        if (m_value == std::numeric_limits<T>::max())
+            this->overflowed();
+        m_value++;
+        return *this;
+    }
+    
+    const Checked& operator--()
+    {
+        if (m_value == std::numeric_limits<T>::min())
+            this->overflowed();
+        m_value--;
+        return *this;
+    }
+    
+    // postfix operators
+    const Checked operator++(int)
+    {
+        if (m_value == std::numeric_limits<T>::max())
+            this->overflowed();
+        return Checked(m_value++);
+    }
+    
+    const Checked operator--(int)
+    {
+        if (m_value == std::numeric_limits<T>::min())
+            this->overflowed();
+        return Checked(m_value--);
+    }
+    
+    // Boolean operators
+    bool operator!() const
+    {
+        if (this->hasOverflowed())
+            CRASH();
+        return !m_value;
+    }
+
+    typedef void* (Checked::*UnspecifiedBoolType);
+    operator UnspecifiedBoolType*() const
+    {
+        if (this->hasOverflowed())
+            CRASH();
+        return (m_value) ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0;
+    }
+
+    // Value accessors. unsafeGet() will crash if there's been an overflow.
+    T unsafeGet() const
+    {
+        if (this->hasOverflowed())
+            CRASH();
+        return m_value;
+    }
+    
+    bool safeGet(T& value) const WARN_UNUSED_RETURN
+    {
+        value = m_value;
+        return this->hasOverflowed();
+    }
+
+    // Mutating assignment
+    template <typename U> const Checked operator+=(U rhs)
+    {
+        if (!safeAdd(m_value, rhs, m_value))
+            this->overflowed();
+        return *this;
+    }
+
+    template <typename U> const Checked operator-=(U rhs)
+    {
+        if (!safeSub(m_value, rhs, m_value))
+            this->overflowed();
+        return *this;
+    }
+
+    template <typename U> const Checked operator*=(U rhs)
+    {
+        if (!safeMultiply(m_value, rhs, m_value))
+            this->overflowed();
+        return *this;
+    }
+    
+    template <typename U, typename V> const Checked operator+=(Checked<U, V> rhs)
+    {
+        if (rhs.hasOverflowed())
+            this->overflowed();
+        return *this += rhs.m_value;
+    }
+
+    template <typename U, typename V> const Checked operator-=(Checked<U, V> rhs)
+    {
+        if (rhs.hasOverflowed())
+            this->overflowed();
+        return *this -= rhs.m_value;
+    }
+
+    template <typename U, typename V> const Checked operator*=(Checked<U, V> rhs)
+    {
+        if (rhs.hasOverflowed())
+            this->overflowed();
+        return *this *= rhs.m_value;
+    }
+
+    // Equality comparisons
+    template <typename V> bool operator==(Checked<T, V> rhs)
+    {
+        return unsafeGet() == rhs.unsafeGet();
+    }
+
+    template <typename U> bool operator==(U rhs)
+    {
+        if (this->hasOverflowed())
+            this->overflowed();
+        return safeEquals(m_value, rhs);
+    }
+    
+    template <typename U, typename V> const Checked operator==(Checked<U, V> rhs)
+    {
+        return unsafeGet() == Checked(rhs.unsafeGet());
+    }
+
+    template <typename U> bool operator!=(U rhs)
+    {
+        return !(*this == rhs);
+    }
+
+private:
+    // Disallow implicit conversion of floating point to integer types
+    Checked(float);
+    Checked(double);
+    void operator=(float);
+    void operator=(double);
+    void operator+=(float);
+    void operator+=(double);
+    void operator-=(float);
+    void operator-=(double);
+    T m_value;
+};
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs)
+{
+    U x = 0;
+    V y = 0;
+    bool overflowed = lhs.safeGet(x) || rhs.safeGet(y);
+    typename Result<U, V>::ResultType result;
+    overflowed |= !safeAdd(x, y, result);
+    if (overflowed)
+        return ResultOverflowed;
+    return result;
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs)
+{
+    U x = 0;
+    V y = 0;
+    bool overflowed = lhs.safeGet(x) || rhs.safeGet(y);
+    typename Result<U, V>::ResultType result;
+    overflowed |= !safeSub(x, y, result);
+    if (overflowed)
+        return ResultOverflowed;
+    return result;
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs)
+{
+    U x = 0;
+    V y = 0;
+    bool overflowed = lhs.safeGet(x) || rhs.safeGet(y);
+    typename Result<U, V>::ResultType result;
+    overflowed |= !safeMultiply(x, y, result);
+    if (overflowed)
+        return ResultOverflowed;
+    return result;
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(Checked<U, OverflowHandler> lhs, V rhs)
+{
+    return lhs + Checked<V, OverflowHandler>(rhs);
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(Checked<U, OverflowHandler> lhs, V rhs)
+{
+    return lhs - Checked<V, OverflowHandler>(rhs);
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(Checked<U, OverflowHandler> lhs, V rhs)
+{
+    return lhs * Checked<V, OverflowHandler>(rhs);
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(U lhs, Checked<V, OverflowHandler> rhs)
+{
+    return Checked<U, OverflowHandler>(lhs) + rhs;
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(U lhs, Checked<V, OverflowHandler> rhs)
+{
+    return Checked<U, OverflowHandler>(lhs) - rhs;
+}
+
+template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(U lhs, Checked<V, OverflowHandler> rhs)
+{
+    return Checked<U, OverflowHandler>(lhs) * rhs;
+}
+
+}
+
+using WTF::Checked;
+using WTF::RecordOverflow;
+
+#endif
index 903c692..c8312aa 100644 (file)
@@ -1491,7 +1491,7 @@ public:
         m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition));
     }
 
-    void atomPatternCharacter(UChar ch, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    void atomPatternCharacter(UChar ch, int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
     {
         if (m_pattern.m_ignoreCase) {
             UChar lo = Unicode::toLower(ch);
@@ -1506,22 +1506,22 @@ public:
         m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType));
     }
 
-    void atomCharacterClass(CharacterClass* characterClass, bool invert, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    void atomCharacterClass(CharacterClass* characterClass, bool invert, int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
     {
         m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition));
 
-        m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType;
         m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
     }
 
-    void atomBackReference(unsigned subpatternId, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    void atomBackReference(unsigned subpatternId, int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
     {
         ASSERT(subpatternId);
 
         m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition));
 
-        m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType;
         m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
     }
@@ -1582,7 +1582,7 @@ public:
         m_currentAlternativeIndex = beginTerm + 1;
     }
 
-    void atomParentheticalAssertionEnd(int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    void atomParentheticalAssertionEnd(int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
     {
         unsigned beginTerm = popParenthesesStack();
         closeAlternative(beginTerm + 1);
@@ -1598,9 +1598,9 @@ public:
         m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
         m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
 
-        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
-        m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
     }
 
@@ -1680,7 +1680,7 @@ public:
         m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation;
     }
 
-    void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0)
+    void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0)
     {
         unsigned beginTerm = popParenthesesStack();
         closeAlternative(beginTerm + 1);
@@ -1706,12 +1706,12 @@ public:
         m_allParenthesesInfo.append(parenthesesDisjunction);
         m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition));
 
-        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
         m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation;
     }
 
-    void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
     {
         unsigned beginTerm = popParenthesesStack();
         closeAlternative(beginTerm + 1);
@@ -1727,13 +1727,13 @@ public:
         m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
         m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
 
-        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
-        m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
     }
 
-    void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
     {
         unsigned beginTerm = popParenthesesStack();
         closeAlternative(beginTerm + 1);
@@ -1749,9 +1749,9 @@ public:
         m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
         m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
 
-        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
-        m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount;
+        m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
         m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
     }
 
index 78a8f34..eb5fdc6 100644 (file)
@@ -107,7 +107,7 @@ struct ByteTerm {
     bool m_invert : 1;
     int inputPosition;
 
-    ByteTerm(UChar ch, int inputPos, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
         : frameLocation(frameLocation)
         , m_capture(false)
         , m_invert(false)
@@ -126,11 +126,11 @@ struct ByteTerm {
 
         atom.patternCharacter = ch;
         atom.quantityType = quantityType;
-        atom.quantityCount = quantityCount;
+        atom.quantityCount = quantityCount.unsafeGet();
         inputPosition = inputPos;
     }
 
-    ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType)
+    ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
         : frameLocation(frameLocation)
         , m_capture(false)
         , m_invert(false)
@@ -150,7 +150,7 @@ struct ByteTerm {
         atom.casedCharacter.lo = lo;
         atom.casedCharacter.hi = hi;
         atom.quantityType = quantityType;
-        atom.quantityCount = quantityCount;
+        atom.quantityCount = quantityCount.unsafeGet();
         inputPosition = inputPos;
     }
 
@@ -204,17 +204,17 @@ struct ByteTerm {
         return term;
     }
 
-    static ByteTerm CheckInput(unsigned count)
+    static ByteTerm CheckInput(Checked<unsigned> count)
     {
         ByteTerm term(TypeCheckInput);
-        term.checkInputCount = count;
+        term.checkInputCount = count.unsafeGet();
         return term;
     }
 
-    static ByteTerm UncheckInput(unsigned count)
+    static ByteTerm UncheckInput(Checked<unsigned> count)
     {
         ByteTerm term(TypeUncheckInput);
-        term.checkInputCount = count;
+        term.checkInputCount = count.unsafeGet();
         return term;
     }
     
index 8d5344f..0649661 100644 (file)
@@ -560,7 +560,7 @@ class YarrGenerator : private MacroAssembler {
             if (term->inputPosition == m_checked)
                 matchDest.append(atEndOfInput());
 
-            readCharacter((term->inputPosition - m_checked), character);
+            readCharacter(term->inputPosition - m_checked, character);
             matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass());
             op.m_jumps.append(jump());
 
@@ -716,10 +716,10 @@ class YarrGenerator : private MacroAssembler {
         const RegisterID countRegister = regT1;
 
         move(index, countRegister);
-        sub32(Imm32(term->quantityCount), countRegister);
+        sub32(Imm32(term->quantityCount.unsafeGet()), countRegister);
 
         Label loop(this);
-        BaseIndex address(input, countRegister, TimesTwo, (term->inputPosition - m_checked + term->quantityCount) * sizeof(UChar));
+        BaseIndex address(input, countRegister, TimesTwo, ((term->inputPosition - m_checked + Checked<int>(term->quantityCount)) * static_cast<int>(sizeof(UChar))).unsafeGet());
 
         if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
             load16(address, character);
@@ -765,7 +765,7 @@ class YarrGenerator : private MacroAssembler {
         if (term->quantityCount == quantifyInfinite)
             jump(loop);
         else
-            branch32(NotEqual, countRegister, Imm32(term->quantityCount)).linkTo(loop, this);
+            branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this);
 
         failures.link(this);
         op.m_reentry = label();
@@ -817,7 +817,7 @@ class YarrGenerator : private MacroAssembler {
 
         nonGreedyFailures.append(atEndOfInput());
         if (term->quantityCount != quantifyInfinite)
-            nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount)));
+            nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet())));
         if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
             readCharacter(term->inputPosition - m_checked, character);
             or32(TrustedImm32(32), character);
@@ -845,7 +845,7 @@ class YarrGenerator : private MacroAssembler {
         const RegisterID character = regT0;
 
         JumpList matchDest;
-        readCharacter((term->inputPosition - m_checked), character);
+        readCharacter(term->inputPosition - m_checked, character);
         matchCharacterClass(character, matchDest, term->characterClass);
 
         if (term->invert())
@@ -869,11 +869,11 @@ class YarrGenerator : private MacroAssembler {
         const RegisterID countRegister = regT1;
 
         move(index, countRegister);
-        sub32(Imm32(term->quantityCount), countRegister);
+        sub32(Imm32(term->quantityCount.unsafeGet()), countRegister);
 
         Label loop(this);
         JumpList matchDest;
-        load16(BaseIndex(input, countRegister, TimesTwo, (term->inputPosition - m_checked + term->quantityCount) * sizeof(UChar)), character);
+        load16(BaseIndex(input, countRegister, TimesTwo, ((term->inputPosition - m_checked + Checked<int>(term->quantityCount)) * static_cast<int>(sizeof(UChar))).unsafeGet()), character);
         matchCharacterClass(character, matchDest, term->characterClass);
 
         if (term->invert())
@@ -919,7 +919,7 @@ class YarrGenerator : private MacroAssembler {
         add32(TrustedImm32(1), countRegister);
         add32(TrustedImm32(1), index);
         if (term->quantityCount != quantifyInfinite) {
-            branch32(NotEqual, countRegister, Imm32(term->quantityCount)).linkTo(loop, this);
+            branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this);
             failures.append(jump());
         } else
             jump(loop);
@@ -972,7 +972,7 @@ class YarrGenerator : private MacroAssembler {
         loadFromFrame(term->frameLocation, countRegister);
 
         nonGreedyFailures.append(atEndOfInput());
-        nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount)));
+        nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet())));
 
         JumpList matchDest;
         readCharacter(term->inputPosition - m_checked, character);
index fc8e8ee..bf150c1 100644 (file)
@@ -511,7 +511,7 @@ public:
     unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition)
     {
         alternative->m_hasFixedSize = true;
-        unsigned currentInputPosition = initialInputPosition;
+        Checked<unsigned> currentInputPosition = initialInputPosition;
 
         for (unsigned i = 0; i < alternative->m_terms.size(); ++i) {
             PatternTerm& term = alternative->m_terms[i];
@@ -520,11 +520,11 @@ public:
             case PatternTerm::TypeAssertionBOL:
             case PatternTerm::TypeAssertionEOL:
             case PatternTerm::TypeAssertionWordBoundary:
-                term.inputPosition = currentInputPosition;
+                term.inputPosition = currentInputPosition.unsafeGet();
                 break;
 
             case PatternTerm::TypeBackReference:
-                term.inputPosition = currentInputPosition;
+                term.inputPosition = currentInputPosition.unsafeGet();
                 term.frameLocation = currentCallFrameSize;
                 currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference;
                 alternative->m_hasFixedSize = false;
@@ -534,7 +534,7 @@ public:
                 break;
 
             case PatternTerm::TypePatternCharacter:
-                term.inputPosition = currentInputPosition;
+                term.inputPosition = currentInputPosition.unsafeGet();
                 if (term.quantityType != QuantifierFixedCount) {
                     term.frameLocation = currentCallFrameSize;
                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter;
@@ -544,7 +544,7 @@ public:
                 break;
 
             case PatternTerm::TypeCharacterClass:
-                term.inputPosition = currentInputPosition;
+                term.inputPosition = currentInputPosition.unsafeGet();
                 if (term.quantityType != QuantifierFixedCount) {
                     term.frameLocation = currentCallFrameSize;
                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass;
@@ -559,18 +559,18 @@ public:
                 if (term.quantityCount == 1 && !term.parentheses.isCopy) {
                     if (term.quantityType != QuantifierFixedCount)
                         currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
-                    currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition);
+                    currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet());
                     // If quantity is fixed, then pre-check its minimum size.
                     if (term.quantityType == QuantifierFixedCount)
                         currentInputPosition += term.parentheses.disjunction->m_minimumSize;
-                    term.inputPosition = currentInputPosition;
+                    term.inputPosition = currentInputPosition.unsafeGet();
                 } else if (term.parentheses.isTerminal) {
                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal;
-                    currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition);
-                    term.inputPosition = currentInputPosition;
+                    currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet());
+                    term.inputPosition = currentInputPosition.unsafeGet();
                 } else {
-                    term.inputPosition = currentInputPosition;
-                    setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition);
+                    term.inputPosition = currentInputPosition.unsafeGet();
+                    setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet());
                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses;
                 }
                 // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length.
@@ -578,9 +578,9 @@ public:
                 break;
 
             case PatternTerm::TypeParentheticalAssertion:
-                term.inputPosition = currentInputPosition;
+                term.inputPosition = currentInputPosition.unsafeGet();
                 term.frameLocation = currentCallFrameSize;
-                currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition);
+                currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet());
                 break;
 
             case PatternTerm::TypeDotStarEnclosure:
@@ -590,7 +590,7 @@ public:
             }
         }
 
-        alternative->m_minimumSize = currentInputPosition - initialInputPosition;
+        alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet();
         return currentCallFrameSize;
     }
 
index 97c3870..e46479e 100644 (file)
@@ -28,6 +28,7 @@
 #define YarrPattern_h
 
 #include <runtime/UString.h>
+#include <wtf/CheckedArithmetic.h>
 #include <wtf/Vector.h>
 #include <wtf/unicode/Unicode.h>
 
@@ -117,7 +118,7 @@ struct PatternTerm {
         } anchors;
     };
     QuantifierType quantityType;
-    unsigned quantityCount;
+    Checked<unsigned> quantityCount;
     int inputPosition;
     unsigned frameLocation;
 
index d510135..e946556 100644 (file)
@@ -1,3 +1,14 @@
+2011-08-30  Oliver Hunt  <oliver@apple.com>
+
+        Add support for checked arithmetic
+        https://bugs.webkit.org/show_bug.cgi?id=67095
+
+        Reviewed by Sam Weinig.
+
+        Add a forwarding header for CheckedArithmetic.h
+
+        * ForwardingHeaders/wtf/CheckedArithmetic.h: Added.
+
 2011-08-31  David Hyatt  <hyatt@apple.com>
 
         https://bugs.webkit.org/show_bug.cgi?id=67300
diff --git a/Source/WebCore/ForwardingHeaders/wtf/CheckedArithmetic.h b/Source/WebCore/ForwardingHeaders/wtf/CheckedArithmetic.h
new file mode 100644 (file)
index 0000000..bc48ae5
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_CheckedArithmetic_h
+#define WebCore_FWD_CheckedArithmetic_h
+#include <JavaScriptCore/CheckedArithmetic.h>
+#endif
index cdd1a79..662ef70 100644 (file)
@@ -1,3 +1,15 @@
+2011-08-30  Oliver Hunt  <oliver@apple.com>
+
+        Add support for checked arithmetic
+        https://bugs.webkit.org/show_bug.cgi?id=67095
+
+        Reviewed by Sam Weinig.
+
+        Add test cases for Checked<>
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/CheckedArithmeticOperations.cpp: Added.
+
 2011-08-31  Adam Barth  <abarth@webkit.org>
 
         Move summary.html to garden-o-matic.html.
index 1124d29..973a392 100644 (file)
@@ -23,6 +23,7 @@
                37DC6791140D7D7600ABCCDB /* DOMRangeOfString.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 37DC678F140D7D3A00ABCCDB /* DOMRangeOfString.html */; };
                4BFDFFA71314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDFFA61314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp */; };
                4BFDFFA9131477770061F24B /* HitTestResultNodeHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDFFA8131477770061F24B /* HitTestResultNodeHandle.cpp */; };
+               A7E69B5E140C117200223412 /* CheckedArithmeticOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E69B5D140C117200223412 /* CheckedArithmeticOperations.cpp */; };
                BC131885117114B600B69727 /* PlatformUtilitiesMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC131884117114B600B69727 /* PlatformUtilitiesMac.mm */; };
                BC131A9B1171316900B69727 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC131A9A1171316900B69727 /* main.mm */; };
                BC131AA9117131FC00B69727 /* TestsController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC131AA8117131FC00B69727 /* TestsController.cpp */; };
                4BFDFFA61314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HitTestResultNodeHandle_Bundle.cpp; sourceTree = "<group>"; };
                4BFDFFA8131477770061F24B /* HitTestResultNodeHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HitTestResultNodeHandle.cpp; sourceTree = "<group>"; };
                8DD76FA10486AA7600D96B5E /* TestWebKitAPI */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TestWebKitAPI; sourceTree = BUILT_PRODUCTS_DIR; };
+               A7E69B5D140C117200223412 /* CheckedArithmeticOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CheckedArithmeticOperations.cpp; sourceTree = "<group>"; };
                BC131883117114A800B69727 /* PlatformUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformUtilities.h; sourceTree = "<group>"; };
                BC131884117114B600B69727 /* PlatformUtilitiesMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformUtilitiesMac.mm; sourceTree = "<group>"; };
                BC131A9A1171316900B69727 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
                                C01363C713C3997300EF3964 /* StringOperators.cpp */,
                                BC90964B125561BF00083756 /* VectorBasic.cpp */,
                                37200B9113A16230007A4FAD /* VectorReverse.cpp */,
+                               A7E69B5D140C117200223412 /* CheckedArithmeticOperations.cpp */,
                        );
                        name = WTF;
                        sourceTree = "<group>";
                                C08587BF13FE956C001EF4E5 /* WebKitAgnosticTest.mm in Sources */,
                                C08587FC13FEC39B001EF4E5 /* InstanceMethodSwizzler.mm in Sources */,
                                C085880013FEC3A6001EF4E5 /* InstanceMethodSwizzler.mm in Sources */,
+                               A7E69B5E140C117200223412 /* CheckedArithmeticOperations.cpp in Sources */,
                                37DC678D140D7C5000ABCCDB /* DOMRangeOfString.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
diff --git a/Tools/TestWebKitAPI/Tests/CheckedArithmeticOperations.cpp b/Tools/TestWebKitAPI/Tests/CheckedArithmeticOperations.cpp
new file mode 100644 (file)
index 0000000..b614004
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 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. AND ITS CONTRIBUTORS ``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 ITS 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.
+ */
+
+#include <JavaScriptCore/CheckedArithmetic.h>
+
+namespace TestWebKitAPI {
+
+#define CheckedArithmeticTest(type, coerceLiteral, MixedSignednessTest) \
+    TEST(WTF, Checked_##type) \
+    { \
+        Checked<type, RecordOverflow> value; \
+        EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); \
+        EXPECT_EQ(std::numeric_limits<type>::max(), (value + std::numeric_limits<type>::max()).unsafeGet()); \
+        EXPECT_EQ(std::numeric_limits<type>::max(), (std::numeric_limits<type>::max() + value).unsafeGet()); \
+        EXPECT_EQ(std::numeric_limits<type>::min(), (value + std::numeric_limits<type>::min()).unsafeGet()); \
+        EXPECT_EQ(std::numeric_limits<type>::min(), (std::numeric_limits<type>::min() + value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value * coerceLiteral(0)).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) * value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value * value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(0)).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) - value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value - value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value++).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(1), (value--).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(1), (++value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (--value).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(10), (value += coerceLiteral(10)).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(100), (value *= coerceLiteral(10)).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(100), value.unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value -= coerceLiteral(100)).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); \
+        value = 10; \
+        EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(10)).unsafeGet()); \
+        EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \
+        value = std::numeric_limits<type>::min(); \
+        EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - coerceLiteral(1))).hasOverflowed()); \
+        EXPECT_EQ(false, (value--).hasOverflowed()); \
+        EXPECT_EQ(true, value.hasOverflowed()); \
+        value = std::numeric_limits<type>::max(); \
+        EXPECT_EQ(false, value.hasOverflowed()); \
+        EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + coerceLiteral(1))).hasOverflowed()); \
+        EXPECT_EQ(false, (value++).hasOverflowed()); \
+        EXPECT_EQ(true, value.hasOverflowed()); \
+        value = std::numeric_limits<type>::max(); \
+        EXPECT_EQ(true, (value += coerceLiteral(1)).hasOverflowed()); \
+        EXPECT_EQ(true, value.hasOverflowed()); \
+        value = 10; \
+        MixedSignednessTest(EXPECT_EQ(coerceLiteral(0), (value + -10).unsafeGet())); \
+        MixedSignednessTest(EXPECT_EQ(0U, (value - 10U).unsafeGet())); \
+        MixedSignednessTest(EXPECT_EQ(coerceLiteral(0), (-10 + value).unsafeGet())); \
+        MixedSignednessTest(EXPECT_EQ(0U, (10U - value).unsafeGet())); \
+        value = std::numeric_limits<type>::min(); \
+        MixedSignednessTest(EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - 1)).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(false, (value--).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \
+        value = std::numeric_limits<type>::max(); \
+        MixedSignednessTest(EXPECT_EQ(false, value.hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1)).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(false, (value++).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \
+        value = std::numeric_limits<type>::max(); \
+        MixedSignednessTest(EXPECT_EQ(true, (value += 1).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \
+        value = std::numeric_limits<type>::min(); \
+        MixedSignednessTest(EXPECT_EQ(true, (value - 1U).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(false, (value--).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \
+        value = std::numeric_limits<type>::max(); \
+        MixedSignednessTest(EXPECT_EQ(false, value.hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1U)).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(false, (value++).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \
+        value = std::numeric_limits<type>::max(); \
+        MixedSignednessTest(EXPECT_EQ(true, (value += 1U).hasOverflowed())); \
+        MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \
+    }
+
+#define CoerceLiteralToUnsigned(x) x##U
+#define CoerceLiteralNop(x) x
+#define AllowMixedSignednessTest(x) x
+#define IgnoreMixedSignednessTest(x)
+CheckedArithmeticTest(int8_t, CoerceLiteralNop, IgnoreMixedSignednessTest)
+CheckedArithmeticTest(int16_t, CoerceLiteralNop, IgnoreMixedSignednessTest)
+CheckedArithmeticTest(int32_t, CoerceLiteralNop, AllowMixedSignednessTest)
+CheckedArithmeticTest(uint32_t, CoerceLiteralToUnsigned, AllowMixedSignednessTest)
+CheckedArithmeticTest(int64_t, CoerceLiteralNop, IgnoreMixedSignednessTest)
+CheckedArithmeticTest(uint64_t, CoerceLiteralToUnsigned, IgnoreMixedSignednessTest)
+
+} // namespace TestWebKitAPI