<commit_before>{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_HADDOCK hide #-}
module Dalvik.SSA.Types (
  DexFile(..),
  Type(..),
  Class(..),
  Field(..),
  Method(..),
  methodSignature,
  methodIsVirtual,
  Parameter(..),
  BasicBlock(..),
  basicBlockInstructions,
  MethodRef(..),
  UniqueId,
  Value(..),
  IsValue(..),
  FromValue(..),
  CastException(..),
  Constant(..),
  constantId,
  constantType,
  Instruction(..),
  InvokeDirectKind(..),
  InvokeVirtualKind(..),
  LL.CmpOp(..),
  LL.IfOp(..),
  LL.Binop(..),
  LL.Unop(..),
  LL.CType(..),
  module Dalvik.AccessFlags
  ) where

import Control.Exception ( Exception )
import Control.Failure
import qualified Data.ByteString as BS
import Data.Function ( on )
import Data.Hashable
import Data.Int ( Int64 )
import Data.Typeable ( Typeable )
import Data.Vector ( Vector )
import qualified Data.Vector as V

import Dalvik.AccessFlags
import Dalvik.ClassHierarchy
-- Low-level instructions
import qualified Dalvik.Instruction as LL

-- | A Dalvik Dex file represented in SSA form.
data DexFile =
  DexFile { dexClasses :: [Class]
          , dexConstants :: [Constant]
          , dexTypes :: [Type]
          }

data Value = InstructionV Instruction
           | ConstantV Constant
           | ParameterV Parameter

instance Eq Value where
  (==) = (==) `on` valueId

instance Ord Value where
  compare = compare `on` valueId

instance Hashable Value where
  hashWithSalt s = hashWithSalt s . valueId

-- | A common interface to the three 'Value'-like types.
class IsValue a where
  valueType :: a -> Type
  valueId :: a -> UniqueId
  toValue :: a -> Value

instance IsValue Value where
  valueId (InstructionV i) = instructionId i
  valueId (ConstantV c) = constantId c
  valueId (ParameterV p) = parameterId p

  valueType (InstructionV i) = instructionType i
  valueType (ConstantV c) = constantType c
  valueType (ParameterV p) = parameterType p

  toValue = id

-- | Convenient and safe casting from 'Value' to a concrete type
-- (either 'Constant', 'Instruction', or 'Parameter').
class FromValue a where
  fromValue :: (Failure CastException f) => Value -> f a

data CastException = CastException String
                   deriving (Eq, Ord, Show, Typeable)

instance Exception CastException


-- FIXME: For now, all numeric constants are integer types because we
-- can't tell (without a type inference pass) what type a constant
-- really is.  Dalvik is untyped.
data Constant = ConstantInt !UniqueId !Int64
              | ConstantString !UniqueId BS.ByteString
              | ConstantClass !UniqueId Type

instance Eq Constant where
  (==) = (==) `on` constantId

instance Ord Constant where
  compare = compare `on` constantId

instance Hashable Constant where
  hashWithSalt s = hashWithSalt s . constantId

constantId :: Constant -> Int
constantId (ConstantInt i _) = i
constantId (ConstantString i _) = i
constantId (ConstantClass i _) = i

constantType :: Constant -> Type
constantType _ = UnknownType

instance IsValue Constant where
  valueId = constantId
  valueType = constantType
  toValue = ConstantV

instance FromValue Constant where
  fromValue (ConstantV c) = return c
  fromValue _ = failure $ CastException "Not a Constant"

data Type = VoidType
          | ByteType
          | ShortType
          | IntType
          | LongType
          | FloatType
          | DoubleType
          | CharType
          | BooleanType
          | ArrayType Type
          | ReferenceType ClassName
          | UnknownType
            -- ^ We use this in cases where we can't deduce a type
            -- during the SSA translation
          deriving (Eq, Ord)

instance Hashable Type where
  hashWithSalt s VoidType = hashWithSalt s (1 :: Int)
  hashWithSalt s ByteType = hashWithSalt s (2 :: Int)
  hashWithSalt s ShortType = hashWithSalt s (3 :: Int)
  hashWithSalt s IntType = hashWithSalt s (4 :: Int)
  hashWithSalt s LongType = hashWithSalt s (5 :: Int)
  hashWithSalt s FloatType = hashWithSalt s (6 :: Int)
  hashWithSalt s DoubleType = hashWithSalt s (7 :: Int)
  hashWithSalt s CharType = hashWithSalt s (8 :: Int)
  hashWithSalt s BooleanType = hashWithSalt s (9 :: Int)
  hashWithSalt s (ArrayType t) = s `hashWithSalt` (10 :: Int) `hashWithSalt` t
  hashWithSalt s (ReferenceType cname) = s `hashWithSalt` (11 :: Int) `hashWithSalt` cname
  hashWithSalt s UnknownType = hashWithSalt s (12 :: Int)

-- | A basic block containing 'Instruction's.  We maintain a count of
-- the phi nodes in the block so that we can efficiently slice out
-- either instructions or phi nodes for separate processing.
--
-- The 'basicBlockNumber' is the local identifier (within a method) of
-- a 'BasicBlock'.  The unique ID is unique among all 'BasicBlock's in
-- the 'DexFile'.
data BasicBlock = BasicBlock { basicBlockId :: UniqueId
                             , basicBlockNumber :: Int
                             , _basicBlockInstructions :: Vector Instruction
                             , basicBlockPhiCount :: Int
                             , basicBlockSuccessors :: [BasicBlock]
                             , basicBlockPredecessors :: [BasicBlock]
                             }

basicBlockInstructions :: BasicBlock -> [Instruction]
basicBlockInstructions = V.toList . _basicBlockInstructions

instance Eq BasicBlock where
  (==) = (==) `on` basicBlockId

instance Ord BasicBlock where
  compare = compare `on` basicBlockId

instance Hashable BasicBlock where
  hashWithSalt s = hashWithSalt s . basicBlockId

type UniqueId = Int

data Instruction = Return { instructionId :: UniqueId
                          , instructionType :: Type
                          , instructionBasicBlock :: BasicBlock
                          , returnValue :: Maybe Value
                          }
                 | MoveException { instructionId :: UniqueId
                                 , instructionType :: Type
                                 , instructionBasicBlock :: BasicBlock
                                 }
                 | MonitorEnter { instructionId :: UniqueId
                                , instructionType :: Type<commit_msg>Add a helper to get the terminator instruction for a block<commit_after>{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_HADDOCK hide #-}
module Dalvik.SSA.Types (
  DexFile(..),
  Type(..),
  Class(..),
  Field(..),
  Method(..),
  methodSignature,
  methodIsVirtual,
  Parameter(..),
  BasicBlock(..),
  basicBlockInstructions,
  basicBlockTerminator,
  MethodRef(..),
  UniqueId,
  Value(..),
  IsValue(..),
  FromValue(..),
  CastException(..),
  Constant(..),
  constantId,
  constantType,
  Instruction(..),
  InvokeDirectKind(..),
  InvokeVirtualKind(..),
  LL.CmpOp(..),
  LL.IfOp(..),
  LL.Binop(..),
  LL.Unop(..),
  LL.CType(..),
  module Dalvik.AccessFlags
  ) where

import Control.Exception ( Exception )
import Control.Failure
import qualified Data.ByteString as BS
import Data.Function ( on )
import Data.Hashable
import Data.Int ( Int64 )
import Data.Typeable ( Typeable )
import Data.Vector ( Vector )
import qualified Data.Vector as V

import Dalvik.AccessFlags
import Dalvik.ClassHierarchy
-- Low-level instructions
import qualified Dalvik.Instruction as LL

-- | A Dalvik Dex file represented in SSA form.
data DexFile =
  DexFile { dexClasses :: [Class]
          , dexConstants :: [Constant]
          , dexTypes :: [Type]
          }

data Value = InstructionV Instruction
           | ConstantV Constant
           | ParameterV Parameter

instance Eq Value where
  (==) = (==) `on` valueId

instance Ord Value where
  compare = compare `on` valueId

instance Hashable Value where
  hashWithSalt s = hashWithSalt s . valueId

-- | A common interface to the three 'Value'-like types.
class IsValue a where
  valueType :: a -> Type
  valueId :: a -> UniqueId
  toValue :: a -> Value

instance IsValue Value where
  valueId (InstructionV i) = instructionId i
  valueId (ConstantV c) = constantId c
  valueId (ParameterV p) = parameterId p

  valueType (InstructionV i) = instructionType i
  valueType (ConstantV c) = constantType c
  valueType (ParameterV p) = parameterType p

  toValue = id

-- | Convenient and safe casting from 'Value' to a concrete type
-- (either 'Constant', 'Instruction', or 'Parameter').
class FromValue a where
  fromValue :: (Failure CastException f) => Value -> f a

data CastException = CastException String
                   deriving (Eq, Ord, Show, Typeable)

instance Exception CastException


-- FIXME: For now, all numeric constants are integer types because we
-- can't tell (without a type inference pass) what type a constant
-- really is.  Dalvik is untyped.
data Constant = ConstantInt !UniqueId !Int64
              | ConstantString !UniqueId BS.ByteString
              | ConstantClass !UniqueId Type

instance Eq Constant where
  (==) = (==) `on` constantId

instance Ord Constant where
  compare = compare `on` constantId

instance Hashable Constant where
  hashWithSalt s = hashWithSalt s . constantId

constantId :: Constant -> Int
constantId (ConstantInt i _) = i
constantId (ConstantString i _) = i
constantId (ConstantClass i _) = i

constantType :: Constant -> Type
constantType _ = UnknownType

instance IsValue Constant where
  valueId = constantId
  valueType = constantType
  toValue = ConstantV

instance FromValue Constant where
  fromValue (ConstantV c) = return c
  fromValue _ = failure $ CastException "Not a Constant"

data Type = VoidType
          | ByteType
          | ShortType
          | IntType
          | LongType
          | FloatType
          | DoubleType
          | CharType
          | BooleanType
          | ArrayType Type
          | ReferenceType ClassName
          | UnknownType
            -- ^ We use this in cases where we can't deduce a type
            -- during the SSA translation
          deriving (Eq, Ord)

instance Hashable Type where
  hashWithSalt s VoidType = hashWithSalt s (1 :: Int)
  hashWithSalt s ByteType = hashWithSalt s (2 :: Int)
  hashWithSalt s ShortType = hashWithSalt s (3 :: Int)
  hashWithSalt s IntType = hashWithSalt s (4 :: Int)
  hashWithSalt s LongType = hashWithSalt s (5 :: Int)
  hashWithSalt s FloatType = hashWithSalt s (6 :: Int)
  hashWithSalt s DoubleType = hashWithSalt s (7 :: Int)
  hashWithSalt s CharType = hashWithSalt s (8 :: Int)
  hashWithSalt s BooleanType = hashWithSalt s (9 :: Int)
  hashWithSalt s (ArrayType t) = s `hashWithSalt` (10 :: Int) `hashWithSalt` t
  hashWithSalt s (ReferenceType cname) = s `hashWithSalt` (11 :: Int) `hashWithSalt` cname
  hashWithSalt s UnknownType = hashWithSalt s (12 :: Int)

-- | A basic block containing 'Instruction's.  We maintain a count of
-- the phi nodes in the block so that we can efficiently slice out
-- either instructions or phi nodes for separate processing.
--
-- The 'basicBlockNumber' is the local identifier (within a method) of
-- a 'BasicBlock'.  The unique ID is unique among all 'BasicBlock's in
-- the 'DexFile'.
data BasicBlock = BasicBlock { basicBlockId :: UniqueId
                             , basicBlockNumber :: Int
                             , _basicBlockInstructions :: Vector Instruction
                             , basicBlockPhiCount :: Int
                             , basicBlockSuccessors :: [BasicBlock]
                             , basicBlockPredecessors :: [BasicBlock]
                             }

-- | The instructions in the 'BasicBlock'
basicBlockInstructions :: BasicBlock -> [Instruction]
basicBlockInstructions = V.toList . _basicBlockInstructions

-- | The last non-phi instruction in the block.  There may not be one
-- if the block is empty.
--
-- Note: we could have this always succeed by adding explicit jumps at
-- the end of empty blocks.
basicBlockTerminator :: BasicBlock -> Maybe Instruction
basicBlockTerminator bb
  | basicBlockPhiCount bb >= len = Nothing
  | otherwise = Just $ insns V.! (len - 1)
  where
    insns = _basicBlockInstructions bb
    len = V.length insns

instance Eq BasicBlock where
  (==) = (==) `on` basicBlockId

instance Ord BasicBlock where
  compare = compare `on` basicBlockId

instance Hashable BasicBlock where
  hashWithSalt s = hashWithSalt s . basicBlockId

type UniqueId = Int

data Instruction = Return { instructionId :: UniqueId
                          , instructionType :: Type
                          , instructionBasicBlock :: BasicBlock
                          , returnValue :: Maybe Value
                          }
                 | MoveException { instructionId :: UniqueId
                                 , instructionType :: Type
                                 , instructionBasicBlock :: BasicBlock
                                 }
                 | MonitorEnter { instructionId :: UniqueId
                                , instructionType :: Type
{'fraction_non_alphanumeric': 0.0888631090487239, 'fraction_numerical': 0.0030162412993039443, 'mean_word_length': 2.5554028045092108, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '479721', 'n_tokens_mistral': 3688, 'n_tokens_neox': 3543, 'n_words': 1240}