module EVM.Debug where

import EVM (bytecode)
import EVM.Expr (bufLength)
import EVM.Solidity (SrcMap(..), SourceCache(..))
import EVM.Types (Contract(..), Addr)

import Control.Arrow (second)
import Data.ByteString (ByteString)
import Data.ByteString qualified as ByteString
import Data.Map (Map)
import Data.Map qualified as Map
import Optics.Core
import Text.PrettyPrint.ANSI.Leijen
import Witch (unsafeInto)

data Mode = Debug | Run | JsonTrace deriving (Mode -> Mode -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Mode -> Mode -> Bool
$c/= :: Mode -> Mode -> Bool
== :: Mode -> Mode -> Bool
$c== :: Mode -> Mode -> Bool
Eq, Int -> Mode -> ShowS
[Mode] -> ShowS
Mode -> FilePath
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [Mode] -> ShowS
$cshowList :: [Mode] -> ShowS
show :: Mode -> FilePath
$cshow :: Mode -> FilePath
showsPrec :: Int -> Mode -> ShowS
$cshowsPrec :: Int -> Mode -> ShowS
Show)

object :: [(Doc, Doc)] -> Doc
object :: [(Doc, Doc)] -> Doc
object [(Doc, Doc)]
xs =
  Doc -> Doc
group forall a b. (a -> b) -> a -> b
$ Doc
lbrace
    forall a. Semigroup a => a -> a -> a
<> Doc
line
    forall a. Semigroup a => a -> a -> a
<> Int -> Doc -> Doc
indent Int
2 ([Doc] -> Doc
sep (Doc -> [Doc] -> [Doc]
punctuate (Char -> Doc
char Char
';') [Doc
k Doc -> Doc -> Doc
<+> Doc
equals Doc -> Doc -> Doc
<+> Doc
v | (Doc
k, Doc
v) <- [(Doc, Doc)]
xs]))
    forall a. Semigroup a => a -> a -> a
<> Doc
line
    forall a. Semigroup a => a -> a -> a
<> Doc
rbrace

prettyContract :: Contract -> Doc
prettyContract :: Contract -> Doc
prettyContract Contract
c =
  [(Doc, Doc)] -> Doc
object
    [ (FilePath -> Doc
text FilePath
"codesize", FilePath -> Doc
text forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> FilePath
show forall a b. (a -> b) -> a -> b
$ (Expr 'Buf -> Expr 'EWord
bufLength (Contract
c forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Getter Contract (Expr 'Buf)
bytecode)))
    , (FilePath -> Doc
text FilePath
"codehash", FilePath -> Doc
text (forall a. Show a => a -> FilePath
show Contract
c.codehash))
    , (FilePath -> Doc
text FilePath
"balance", Int -> Doc
int (forall target source.
(HasCallStack, TryFrom source target, Show source, Typeable source,
 Typeable target) =>
source -> target
unsafeInto Contract
c.balance))
    , (FilePath -> Doc
text FilePath
"nonce", Int -> Doc
int (forall target source.
(HasCallStack, TryFrom source target, Show source, Typeable source,
 Typeable target) =>
source -> target
unsafeInto Contract
c.nonce))
    ]

prettyContracts :: Map Addr Contract -> Doc
prettyContracts :: Map Addr Contract -> Doc
prettyContracts Map Addr Contract
x =
  [(Doc, Doc)] -> Doc
object
    (forall a b. (a -> b) -> [a] -> [b]
map (\(Addr
a, Contract
b) -> (FilePath -> Doc
text (forall a. Show a => a -> FilePath
show Addr
a), Contract -> Doc
prettyContract Contract
b))
     (forall k a. Map k a -> [(k, a)]
Map.toList Map Addr Contract
x))

srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int)
srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int)
srcMapCodePos SourceCache
cache SrcMap
sm =
  forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second ByteString -> Int
f) forall a b. (a -> b) -> a -> b
$ SourceCache
cache.files forall k s (is :: IxList) a.
Is k An_AffineFold =>
s -> Optic' k is s a -> Maybe a
^? forall m. Ixed m => Index m -> Optic' (IxKind m) NoIx m (IxValue m)
ix SrcMap
sm.file
  where
    f :: ByteString -> Int
f ByteString
v = Word8 -> ByteString -> Int
ByteString.count Word8
0xa (Int -> ByteString -> ByteString
ByteString.take SrcMap
sm.offset ByteString
v) forall a. Num a => a -> a -> a
+ Int
1

srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString
srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString
srcMapCode SourceCache
cache SrcMap
sm =
  forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (FilePath, ByteString) -> ByteString
f forall a b. (a -> b) -> a -> b
$ SourceCache
cache.files forall k s (is :: IxList) a.
Is k An_AffineFold =>
s -> Optic' k is s a -> Maybe a
^? forall m. Ixed m => Index m -> Optic' (IxKind m) NoIx m (IxValue m)
ix SrcMap
sm.file
  where
    f :: (FilePath, ByteString) -> ByteString
f (FilePath
_, ByteString
v) = Int -> ByteString -> ByteString
ByteString.take (forall a. Ord a => a -> a -> a
min Int
80 SrcMap
sm.length) (Int -> ByteString -> ByteString
ByteString.drop SrcMap
sm.offset ByteString
v)