Commits

Bryan O'Sullivan committed 4224f42

Capture and report failure properly during unescaping

Comments (0)

Files changed (1)

Data/Aeson/Parser.hs

 import Blaze.ByteString.Builder.Char.Utf8 (fromChar)
 import Blaze.ByteString.Builder.Word (fromWord8)
 import Control.Applicative as A
-import Control.Monad (when)
 import Data.Aeson.Types (Value(..))
 import Data.Attoparsec.Char8
 import Data.Bits ((.|.), shiftL)
                 _            -> fail "unexpected failure"
 
 unescape :: Parser ByteString
-unescape = toByteString <$> go mempty where
-  go acc = do
+unescape = toByteString <$> go (Right mempty) where
+  go (Right acc) = do
     h <- A.takeWhile (/=backslash)
     let rest = do
           start <- A.take 2
               escape = case B.findIndex (==t) "\"\\/ntbrfu" of
                          Just i -> i
                          _      -> 255
-          when (slash /= backslash || escape == 255) $
-            fail "invalid JSON escape sequence"
-          let continue m = go (acc `mappend` fromByteString h `mappend` m)
-              {-# INLINE continue #-}
-          if t /= 117 -- 'u'
-            then continue (fromWord8 (B.unsafeIndex mapping escape))
-            else do
-                 a <- hexQuad
-                 if a < 0xd800 || a > 0xdfff
-                   then continue (fromChar (chr a))
-                   else do
-                     b <- string "\\u" *> hexQuad
-                     if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
-                       then let !c = ((a - 0xd800) `shiftL` 10) + (b - 0xdc00) +
-                                     0x10000
-                            in continue (fromChar (chr c))
-                       else fail "invalid UTF-16 surrogates"
+          if slash /= backslash || escape == 255
+            then go (Left "invalid JSON escape sequence")
+            else flip (<|>) (go (Left "invalid Unicode escape sequence")) $ do
+            let cont m = go (Right (acc `mappend` fromByteString h `mappend` m))
+                {-# INLINE cont #-}
+            if t /= 117 -- 'u'
+              then cont (fromWord8 (B.unsafeIndex mapping escape))
+              else do
+                   a <- hexQuad
+                   if a < 0xd800 || a > 0xdfff
+                     then cont (fromChar (chr a))
+                     else do
+                       b <- string "\\u" *> hexQuad
+                       if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
+                         then let !c = ((a - 0xd800) `shiftL` 10) +
+                                       (b - 0xdc00) + 0x10000
+                              in cont (fromChar (chr c))
+                         else fail "invalid UTF-16 surrogates"
     rest <|> return (acc `mappend` fromByteString h)
+  go (Left err) = fail err
   mapping = "\"\\/\n\t\b\r\f"
 
 hexQuad :: Parser Int
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.