module Blip.Bytecode
( decode, encode, Opcode (..), Bytecode (..),
BytecodeArg (..), BytecodeSeq (..), bytecodeSize ) where
import Data.Word (Word8, Word16)
import Data.ByteString.Lazy as B
( ByteString, unpack, pack )
import Data.Map as Map hiding (map)
import Data.Bits ((.&.), shiftL, shiftR)
import Text.PrettyPrint
( text, (<+>), Doc, int, vcat )
import Blip.Pretty (Pretty (..))
data Opcode
= POP_TOP
| ROT_TWO
| ROT_THREE
| DUP_TOP
| DUP_TOP_TWO
| NOP
| UNARY_POSITIVE
| UNARY_NEGATIVE
| UNARY_NOT
| UNARY_INVERT
| BINARY_POWER
| BINARY_MULTIPLY
| BINARY_MODULO
| BINARY_ADD
| BINARY_SUBTRACT
| BINARY_SUBSCR
| BINARY_FLOOR_DIVIDE
| BINARY_TRUE_DIVIDE
| INPLACE_FLOOR_DIVIDE
| INPLACE_TRUE_DIVIDE
| STORE_MAP
| INPLACE_ADD
| INPLACE_SUBTRACT
| INPLACE_MULTIPLY
| INPLACE_MODULO
| STORE_SUBSCR
| DELETE_SUBSCR
| BINARY_LSHIFT
| BINARY_RSHIFT
| BINARY_AND
| BINARY_XOR
| BINARY_OR
| INPLACE_POWER
| GET_ITER
| STORE_LOCALS
| PRINT_EXPR
| LOAD_BUILD_CLASS
| YIELD_FROM
| INPLACE_LSHIFT
| INPLACE_RSHIFT
| INPLACE_AND
| INPLACE_XOR
| INPLACE_OR
| BREAK_LOOP
| WITH_CLEANUP
| RETURN_VALUE
| IMPORT_STAR
| YIELD_VALUE
| POP_BLOCK
| END_FINALLY
| POP_EXCEPT
| STORE_NAME
| DELETE_NAME
| UNPACK_SEQUENCE
| FOR_ITER
| UNPACK_EX
| STORE_ATTR
| DELETE_ATTR
| STORE_GLOBAL
| DELETE_GLOBAL
| LOAD_CONST
| LOAD_NAME
| BUILD_TUPLE
| BUILD_LIST
| BUILD_SET
| BUILD_MAP
| LOAD_ATTR
| COMPARE_OP
| IMPORT_NAME
| IMPORT_FROM
| JUMP_FORWARD
| JUMP_IF_FALSE_OR_POP
| JUMP_IF_TRUE_OR_POP
| JUMP_ABSOLUTE
| POP_JUMP_IF_FALSE
| POP_JUMP_IF_TRUE
| LOAD_GLOBAL
| CONTINUE_LOOP
| SETUP_LOOP
| SETUP_EXCEPT
| SETUP_FINALLY
| LOAD_FAST
| STORE_FAST
| DELETE_FAST
| RAISE_VARARGS
| CALL_FUNCTION
| MAKE_FUNCTION
| BUILD_SLICE
| MAKE_CLOSURE
| LOAD_CLOSURE
| LOAD_DEREF
| STORE_DEREF
| DELETE_DEREF
| CALL_FUNCTION_VAR
| CALL_FUNCTION_KW
| CALL_FUNCTION_VAR_KW
| SETUP_WITH
| EXTENDED_ARG
| LIST_APPEND
| SET_ADD
| MAP_ADD
deriving (Eq, Ord, Show)
opcodeToWord8 :: Map.Map Opcode Word8
opcodeToWord8 = Map.fromList opcodeList
word8ToOpcode :: Map.Map Word8 Opcode
word8ToOpcode = Map.fromList [ (y, x) | (x, y) <- opcodeList ]
opcodeList :: [(Opcode, Word8)]
opcodeList = [
(POP_TOP, 1),
(ROT_TWO, 2),
(ROT_THREE, 3),
(DUP_TOP, 4),
(DUP_TOP_TWO, 5),
(NOP, 9),
(UNARY_POSITIVE, 10),
(UNARY_NEGATIVE, 11),
(UNARY_NOT, 12),
(UNARY_INVERT, 15),
(BINARY_POWER, 19),
(BINARY_MULTIPLY, 20),
(BINARY_MODULO, 22),
(BINARY_ADD, 23),
(BINARY_SUBTRACT, 24),
(BINARY_SUBSCR, 25),
(BINARY_FLOOR_DIVIDE, 26),
(BINARY_TRUE_DIVIDE, 27),
(INPLACE_FLOOR_DIVIDE, 28),
(INPLACE_TRUE_DIVIDE, 29),
(STORE_MAP, 54),
(INPLACE_ADD, 55),
(INPLACE_SUBTRACT, 56),
(INPLACE_MULTIPLY, 57),
(INPLACE_MODULO, 59),
(STORE_SUBSCR, 60),
(DELETE_SUBSCR, 61),
(BINARY_LSHIFT, 62),
(BINARY_RSHIFT, 63),
(BINARY_AND, 64),
(BINARY_XOR, 65),
(BINARY_OR, 66),
(INPLACE_POWER, 67),
(GET_ITER, 68),
(STORE_LOCALS, 69),
(PRINT_EXPR, 70),
(LOAD_BUILD_CLASS, 71),
(YIELD_FROM, 72),
(INPLACE_LSHIFT, 75),
(INPLACE_RSHIFT, 76),
(INPLACE_AND, 77),
(INPLACE_XOR, 78),
(INPLACE_OR, 79),
(BREAK_LOOP, 80),
(WITH_CLEANUP, 81),
(RETURN_VALUE, 83),
(IMPORT_STAR, 84),
(YIELD_VALUE, 86),
(POP_BLOCK, 87),
(END_FINALLY, 88),
(POP_EXCEPT, 89),
(STORE_NAME, 90),
(DELETE_NAME, 91),
(UNPACK_SEQUENCE, 92),
(FOR_ITER, 93),
(UNPACK_EX, 94),
(STORE_ATTR, 95),
(DELETE_ATTR, 96),
(STORE_GLOBAL, 97),
(DELETE_GLOBAL, 98),
(LOAD_CONST, 100),
(LOAD_NAME, 101),
(BUILD_TUPLE, 102),
(BUILD_LIST, 103),
(BUILD_SET, 104),
(BUILD_MAP, 105),
(LOAD_ATTR, 106),
(COMPARE_OP, 107),
(IMPORT_NAME, 108),
(IMPORT_FROM, 109),
(JUMP_FORWARD, 110),
(JUMP_IF_FALSE_OR_POP, 111),
(JUMP_IF_TRUE_OR_POP, 112),
(JUMP_ABSOLUTE, 113),
(POP_JUMP_IF_FALSE, 114),
(POP_JUMP_IF_TRUE, 115),
(LOAD_GLOBAL, 116),
(CONTINUE_LOOP, 119),
(SETUP_LOOP, 120),
(SETUP_EXCEPT, 121),
(SETUP_FINALLY, 122),
(LOAD_FAST, 124),
(STORE_FAST, 125),
(DELETE_FAST, 126),
(RAISE_VARARGS, 130),
(CALL_FUNCTION, 131),
(MAKE_FUNCTION, 132),
(BUILD_SLICE, 133),
(MAKE_CLOSURE, 134),
(LOAD_CLOSURE, 135),
(LOAD_DEREF, 136),
(STORE_DEREF, 137),
(DELETE_DEREF, 138),
(CALL_FUNCTION_VAR, 140),
(CALL_FUNCTION_KW, 141),
(CALL_FUNCTION_VAR_KW, 142),
(SETUP_WITH, 143),
(EXTENDED_ARG, 144),
(LIST_APPEND, 145),
(SET_ADD, 146),
(MAP_ADD, 147)
]
data BytecodeArg
= Arg16 Word16
deriving Show
instance Pretty BytecodeArg where
pretty (Arg16 w) = pretty w
data Bytecode =
Bytecode
{ opcode :: Opcode
, args :: Maybe BytecodeArg
}
deriving Show
instance Pretty Bytecode where
pretty (Bytecode { opcode = o, args = a }) = (text $ show o) <+> pretty a
bytecodeSize :: Bytecode -> Int
bytecodeSize (Bytecode { args = a }) =
case a of
Nothing -> 1
Just (Arg16 _) -> 3
newtype BytecodeSeq = BytecodeSeq [Bytecode]
instance Pretty BytecodeSeq where
pretty (BytecodeSeq bcs) =
vcat $ map prettyBcOffset bcsOffsets
where
offsets = scanl (\x y -> x + bytecodeSize y) 0 bcs
bcsOffsets = zip offsets bcs
prettyBcOffset :: (Int, Bytecode) -> Doc
prettyBcOffset (i, bc) = int i <+> pretty bc
hasArg :: Opcode -> Bool
hasArg = (>= STORE_NAME)
decode :: ByteString -> [Bytecode]
decode = decodeWords . B.unpack
encode :: [Bytecode] -> ByteString
encode = B.pack . concatMap encodeBytecode
encodeBytecode :: Bytecode -> [Word8]
encodeBytecode (Bytecode opcode arg)
= case Map.lookup opcode opcodeToWord8 of
Nothing -> error ("bad opcode: " ++ show opcode)
Just w1 ->
case arg of
Nothing -> [w1]
Just (Arg16 arg16) ->
let (w2, w3) = word16ToWord8s arg16
in [w1, w2, w3]
decodeWords :: [Word8] -> [Bytecode]
decodeWords [] = []
decodeWords (w:ws) =
case Map.lookup w word8ToOpcode of
Nothing -> error ("bad opcode: " ++ show (w:ws))
Just opcode ->
if hasArg opcode
then
case ws of
w1:w2:rest ->
let arg16 = Arg16 $ word8sToWord16 w1 w2 in
Bytecode opcode (Just arg16) : decodeWords rest
_other ->
error ("truncated bytecode stream: " ++ show (w:ws))
else
Bytecode opcode Nothing : decodeWords ws
word8sToWord16 :: Word8 -> Word8 -> Word16
word8sToWord16 w1 w2 = (fromIntegral w1) + (fromIntegral w2 `shiftL` 8)
word16ToWord8s :: Word16 -> (Word8, Word8)
word16ToWord8s w1 = (w2, w3)
where
w2 = fromIntegral (w1 .&. 255)
w3 = fromIntegral (w1 `shiftR` 8)