-- |
-- Module: EVM.Opcode.Positional
-- Copyright: 2018 Simon Shine
-- Maintainer: Simon Shine <shreddedglory@gmail.com>
-- License: MIT
--
-- This module exposes the `PositionalOpcode` type for expressing Ethereum
-- VM opcodes where jumps and jumpdests are annotated with the byte position
-- of the translated opcode.
--
-- This representation is useful for when generating code that refers to the
-- size of itself or other chunks of code.  E.g. the CODECOPY segment of an
-- Ethereum contract must refer to the size of the code being copied, and
-- determining the size of a jump is trivial when it's annotated with the
-- destination address.

module EVM.Opcode.Positional
  ( Position
  , PositionalOpcode
  , translate
  , jumpSize
  ) where

import EVM.Opcode (Opcode, Opcode'(..), jump, jumpi, jumpdest, concrete, opcodeSize)

-- | The position of an Opcode.
type Position = Word

-- | A 'PositionalOpcode' has byte positions annotated at 'JUMP', 'JUMPI'
-- and 'JUMPDEST'; on 'JUMP' and 'JUMPI' the positions denote where they
-- jump to, and on 'JUMPDEST' they denote the position of the opcode itself.
type PositionalOpcode = Opcode' Position

-- | Translate a 'PositionalOpcode' into an 'Opcode' by converting the position
-- into a 'PUSH' instruction.
translate :: [PositionalOpcode] -> [Opcode]
translate :: [PositionalOpcode] -> [Opcode]
translate = (PositionalOpcode -> [Opcode]) -> [PositionalOpcode] -> [Opcode]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap PositionalOpcode -> [Opcode]
inline
  where
    inline :: PositionalOpcode -> [Opcode]
    inline :: PositionalOpcode -> [Opcode]
inline (JUMP Position
addr) = [ Word256 -> Opcode
forall j. Word256 -> Opcode' j
PUSH (Position -> Word256
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
addr), Opcode
jump ]
    inline (JUMPI Position
addr) = [ Word256 -> Opcode
forall j. Word256 -> Opcode' j
PUSH (Position -> Word256
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
addr), Opcode
jumpi ]
    inline (JUMPDEST Position
_) = [ Opcode
jumpdest ]
    inline PositionalOpcode
opcode = [ PositionalOpcode -> Opcode
forall a. Opcode' a -> Opcode
concrete PositionalOpcode
opcode ]

-- | The size of a jump to some absolute position.
jumpSize :: Num i => Position -> i
jumpSize :: Position -> i
jumpSize Position
pos = Opcode -> i
forall i. Num i => Opcode -> i
opcodeSize (Word256 -> Opcode
forall j. Word256 -> Opcode' j
PUSH (Position -> Word256
forall a b. (Integral a, Num b) => a -> b
fromIntegral Position
pos)) i -> i -> i
forall a. Num a => a -> a -> a
+ Opcode -> i
forall i. Num i => Opcode -> i
opcodeSize Opcode
jump