forked from GitHub/haskell-wasm
Compare commits
1 Commits
simd
..
spec-1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 825a24d59c |
@@ -8,5 +8,3 @@ dist-newstyle/
|
||||
doc/
|
||||
setup-config
|
||||
wasm-*-docs.tar.gz
|
||||
cache
|
||||
packagedb
|
||||
@@ -20,10 +20,9 @@
|
||||
* [ ] Text Representation pretty-printer
|
||||
* [ ] Command line tool for calling interpreter/compiler/validator
|
||||
* [ ] Codegen interface for type enforced generating valid WASM code
|
||||
* [ ] Support for building if, loop, block
|
||||
|
||||
## Development
|
||||
Clone sources to directory and use `stack` for running tests:
|
||||
Clond sources to directory and use `stack` for running tests:
|
||||
```
|
||||
stack build && stack test
|
||||
```
|
||||
|
||||
+20
-168
@@ -1,7 +1,6 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
module Language.Wasm.Binary (
|
||||
dumpModule,
|
||||
@@ -17,8 +16,6 @@ import Data.Bits
|
||||
import Data.Word (Word8, Word32, Word64)
|
||||
import Data.Int (Int8, Int32, Int64)
|
||||
import Data.Serialize
|
||||
import Control.Monad (when)
|
||||
import Data.Primitive.ByteArray as BA
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as LBS
|
||||
import qualified Data.Text.Lazy as TL
|
||||
@@ -224,7 +221,6 @@ instance Serialize ValueType where
|
||||
put I64 = putWord8 0x7E
|
||||
put F32 = putWord8 0x7D
|
||||
put F64 = putWord8 0x7C
|
||||
put V128 = putWord8 0x7B
|
||||
|
||||
get = do
|
||||
op <- getWord8
|
||||
@@ -233,7 +229,6 @@ instance Serialize ValueType where
|
||||
0x7E -> return I64
|
||||
0x7D -> return F32
|
||||
0x7C -> return F64
|
||||
0x7B -> return V128
|
||||
_ -> fail "unexpected byte in value type position"
|
||||
|
||||
instance Serialize FuncType where
|
||||
@@ -249,13 +244,7 @@ instance Serialize FuncType where
|
||||
|
||||
instance Serialize ElemType where
|
||||
put FuncRef = putWord8 0x70
|
||||
put ExternRef = putWord8 0x6F
|
||||
get = do
|
||||
op <- getWord8
|
||||
case op of
|
||||
0x70 -> return FuncRef
|
||||
0x69 -> return ExternRef
|
||||
_ -> fail "unknown reference type"
|
||||
get = byteGuard 0x70 >> return FuncRef
|
||||
|
||||
instance Serialize Limit where
|
||||
put (Limit min Nothing) = putWord8 0x00 >> putULEB128 min
|
||||
@@ -331,17 +320,10 @@ instance Serialize Index where
|
||||
put (Index idx) = putULEB128 idx
|
||||
get = Index <$> getULEB128 32
|
||||
|
||||
newtype Expr = Expr { unExpr :: Expression } deriving (Show, Eq)
|
||||
|
||||
instance Serialize Expr where
|
||||
put (Expr expr) = putExpression expr
|
||||
get = Expr <$> getExpression
|
||||
|
||||
instance Serialize MemArg where
|
||||
put MemArg { align, offset } = putULEB128 align >> putULEB128 offset
|
||||
get = do
|
||||
align <- getULEB128 32
|
||||
when (align >= 32) $ fail "malformed memop flags"
|
||||
offset <- getULEB128 32
|
||||
return $ MemArg { align, offset }
|
||||
|
||||
@@ -371,50 +353,16 @@ instance Serialize (Instruction Natural) where
|
||||
put (BrTable labels label) = putWord8 0x0E >> putVec (map Index labels) >> putULEB128 label
|
||||
put Return = putWord8 0x0F
|
||||
put (Call funcIdx) = putWord8 0x10 >> putULEB128 funcIdx
|
||||
put (CallIndirect tableIdx typeIdx) = putWord8 0x11 >> putULEB128 typeIdx >> putULEB128 tableIdx
|
||||
-- Reference instructions
|
||||
put (RefNull refType) = putWord8 0xD0 >> put refType
|
||||
put RefIsNull = putWord8 0xD1
|
||||
put (RefFunc index) = putWord8 0xD2 >> putULEB128 index
|
||||
put (CallIndirect typeIdx) = putWord8 0x11 >> putULEB128 typeIdx >> putWord8 0x00
|
||||
-- Parametric instructions
|
||||
put Drop = putWord8 0x1A
|
||||
put (Select Nothing) = putWord8 0x1B
|
||||
put (Select (Just types)) = putWord8 0x1C >> putVec types
|
||||
put Select = putWord8 0x1B
|
||||
-- Variable instructions
|
||||
put (GetLocal idx) = putWord8 0x20 >> putULEB128 idx
|
||||
put (SetLocal idx) = putWord8 0x21 >> putULEB128 idx
|
||||
put (TeeLocal idx) = putWord8 0x22 >> putULEB128 idx
|
||||
put (GetGlobal idx) = putWord8 0x23 >> putULEB128 idx
|
||||
put (SetGlobal idx) = putWord8 0x24 >> putULEB128 idx
|
||||
-- Table instructions
|
||||
put (TableGet idx) = putWord8 0x25 >> putULEB128 idx
|
||||
put (TableSet idx) = putWord8 0x26 >> putULEB128 idx
|
||||
put (TableInit tableIdx elemIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x0C :: Word32)
|
||||
putULEB128 tableIdx
|
||||
putULEB128 elemIdx
|
||||
put (ElemDrop elemIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x0D :: Word32)
|
||||
putULEB128 elemIdx
|
||||
put (TableCopy fromIdx toIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x0E :: Word32)
|
||||
putULEB128 fromIdx
|
||||
putULEB128 toIdx
|
||||
put (TableGrow tableIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x0F :: Word32)
|
||||
putULEB128 tableIdx
|
||||
put (TableSize tableIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x10 :: Word32)
|
||||
putULEB128 tableIdx
|
||||
put (TableFill tableIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x11 :: Word32)
|
||||
putULEB128 tableIdx
|
||||
-- Memory instructions
|
||||
put (I32Load memArg) = putWord8 0x28 >> put memArg
|
||||
put (I64Load memArg) = putWord8 0x29 >> put memArg
|
||||
@@ -439,35 +387,13 @@ instance Serialize (Instruction Natural) where
|
||||
put (I64Store8 memArg) = putWord8 0x3C >> put memArg
|
||||
put (I64Store16 memArg) = putWord8 0x3D >> put memArg
|
||||
put (I64Store32 memArg) = putWord8 0x3E >> put memArg
|
||||
put MemorySize = putWord8 0x3F >> putWord8 0x00
|
||||
put MemoryGrow = putWord8 0x40 >> putWord8 0x00
|
||||
put (MemoryInit dataIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x08 :: Word32)
|
||||
putULEB128 dataIdx
|
||||
putWord8 0
|
||||
put (DataDrop dataIdx) = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x09 :: Word32)
|
||||
putULEB128 dataIdx
|
||||
put MemoryCopy = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x0A :: Word32)
|
||||
putWord8 0
|
||||
putWord8 0
|
||||
put MemoryFill = do
|
||||
putWord8 0xFC
|
||||
putULEB128 (0x0B :: Word32)
|
||||
putWord8 0
|
||||
put CurrentMemory = putWord8 0x3F >> putWord8 0x00
|
||||
put GrowMemory = putWord8 0x40 >> putWord8 0x00
|
||||
-- Numeric instructions
|
||||
put (I32Const val) = putWord8 0x41 >> putSLEB128 (asInt32 val)
|
||||
put (I64Const val) = putWord8 0x42 >> putSLEB128 (asInt64 val)
|
||||
put (F32Const val) = putWord8 0x43 >> putFloat32le val
|
||||
put (F64Const val) = putWord8 0x44 >> putFloat64le val
|
||||
put (V128Const val) = do
|
||||
putWord8 0xFD
|
||||
putWord8 12
|
||||
put $ BA.foldrByteArray @Word8 (:) [] val
|
||||
put I32Eqz = putWord8 0x45
|
||||
put (IRelOp BS32 IEq) = putWord8 0x46
|
||||
put (IRelOp BS32 INe) = putWord8 0x47
|
||||
@@ -627,15 +553,11 @@ instance Serialize (Instruction Natural) where
|
||||
0x10 -> Call <$> getULEB128 32
|
||||
0x11 -> do
|
||||
typeIdx <- getULEB128 32
|
||||
tableIdx <- getULEB128 32
|
||||
return $ CallIndirect tableIdx typeIdx
|
||||
-- Reference instructions
|
||||
0xD0 -> RefNull <$> get
|
||||
0xD1 -> return RefIsNull
|
||||
0xD2 -> RefFunc <$> getULEB128 32
|
||||
byteGuard 0x00
|
||||
return $ CallIndirect typeIdx
|
||||
-- Parametric instructions
|
||||
0x1A -> return $ Drop
|
||||
0x1B -> return $ Select Nothing
|
||||
0x1B -> return $ Select
|
||||
-- Variable instructions
|
||||
0x20 -> GetLocal <$> getULEB128 32
|
||||
0x21 -> SetLocal <$> getULEB128 32
|
||||
@@ -666,8 +588,8 @@ instance Serialize (Instruction Natural) where
|
||||
0x3C -> I64Store8 <$> get
|
||||
0x3D -> I64Store16 <$> get
|
||||
0x3E -> I64Store32 <$> get
|
||||
0x3F -> byteGuard 0x00 >> (return $ MemorySize)
|
||||
0x40 -> byteGuard 0x00 >> (return $ MemoryGrow)
|
||||
0x3F -> byteGuard 0x00 >> (return $ CurrentMemory)
|
||||
0x40 -> byteGuard 0x00 >> (return $ GrowMemory)
|
||||
-- Numeric instructions
|
||||
0x41 -> I32Const <$> getSLEB128 32
|
||||
0x42 -> I64Const <$> getSLEB128 64
|
||||
@@ -813,14 +735,7 @@ instance Serialize (Instruction Natural) where
|
||||
0x06 -> return $ ITruncSatFS BS64 BS64
|
||||
0x07 -> return $ ITruncSatFU BS64 BS64
|
||||
_ -> fail "Unknown byte value after misc instruction byte"
|
||||
0xFD -> do -- simd
|
||||
ext <- getULEB128 32
|
||||
case (ext :: Word32) of
|
||||
0x0C -> do
|
||||
bytes <- getByteString 16
|
||||
return $ V128Const $ BA.byteArrayFromListN 16 $ BS.unpack bytes
|
||||
_ -> fail "Unknown byte value after simd instruction byte"
|
||||
byte -> fail $ "Unknown byte value in place of instruction opcode: " ++ (show byte)
|
||||
_ -> fail "Unknown byte value in place of instruction opcode"
|
||||
|
||||
putExpression :: Expression -> Put
|
||||
putExpression expr = do
|
||||
@@ -878,56 +793,11 @@ instance Serialize Export where
|
||||
get = Export <$> getName <*> get
|
||||
|
||||
instance Serialize ElemSegment where
|
||||
put (ElemSegment elemType Passive elements) = do
|
||||
putWord8 0x05
|
||||
put elemType
|
||||
putVec $ map Expr elements
|
||||
put (ElemSegment elemType (Active tableIndex offset) elements) = do
|
||||
putWord8 0x06
|
||||
put (ElemSegment tableIndex offset funcIndexes) = do
|
||||
putULEB128 tableIndex
|
||||
putExpression offset
|
||||
put elemType
|
||||
putVec $ map Expr elements
|
||||
put (ElemSegment elemType Declarative elements) = do
|
||||
putWord8 0x07
|
||||
put elemType
|
||||
putVec $ map Expr elements
|
||||
|
||||
get = do
|
||||
let funcIndexes = map ((:[]) . RefFunc . unIndex) <$> getVec
|
||||
let elemKind = byteGuard 0x00 >> return FuncRef
|
||||
op <- getULEB128 32
|
||||
case (op :: Word8) of
|
||||
0x00 -> do
|
||||
offset <- getExpression
|
||||
ElemSegment FuncRef (Active 0 offset) <$> funcIndexes
|
||||
0x01 -> do
|
||||
elemType <- elemKind
|
||||
ElemSegment elemType Passive <$> funcIndexes
|
||||
0x02 -> do
|
||||
tableIndex <- getULEB128 32
|
||||
offset <- getExpression
|
||||
elemType <- elemKind
|
||||
ElemSegment elemType (Active tableIndex offset) <$> funcIndexes
|
||||
0x03 -> do
|
||||
elemType <- elemKind
|
||||
ElemSegment elemType Declarative <$> funcIndexes
|
||||
0x04 -> do
|
||||
offset <- getExpression
|
||||
ElemSegment FuncRef (Active 0 offset) <$> funcIndexes
|
||||
0x05 -> do
|
||||
elemType <- get
|
||||
ElemSegment elemType Passive . map unExpr <$> getVec
|
||||
0x06 -> do
|
||||
tableIndex <- getULEB128 32
|
||||
offset <- getExpression
|
||||
elemType <- get
|
||||
ElemSegment elemType (Active tableIndex offset) <$> getVec
|
||||
0x07 -> do
|
||||
elemType <- get
|
||||
ElemSegment elemType Declarative <$> getVec
|
||||
_ ->
|
||||
fail "unknown element segment type"
|
||||
putVec $ map Index funcIndexes
|
||||
get = ElemSegment <$> getULEB128 32 <*> getExpression <*> (map unIndex <$> getVec)
|
||||
|
||||
data LocalTypeRange = LocalTypeRange Natural ValueType deriving (Show, Eq)
|
||||
|
||||
@@ -954,35 +824,17 @@ instance Serialize Function where
|
||||
return $ Function 0 locals body
|
||||
|
||||
instance Serialize DataSegment where
|
||||
put (DataSegment (ActiveData memIdx offset) init) = do
|
||||
putWord8 0x02
|
||||
put (DataSegment memIdx offset init) = do
|
||||
putULEB128 memIdx
|
||||
putExpression offset
|
||||
putULEB128 $ LBS.length init
|
||||
putLazyByteString init
|
||||
put (DataSegment PassiveData init) = do
|
||||
putWord8 0x01
|
||||
putULEB128 $ LBS.length init
|
||||
putLazyByteString init
|
||||
get = do
|
||||
op <- getULEB128 32
|
||||
case (op :: Word8) of
|
||||
0x00 -> do
|
||||
offset <- getExpression
|
||||
len <- getULEB128 32
|
||||
init <- getLazyByteString len
|
||||
return $ DataSegment (ActiveData 0 offset) init
|
||||
0x01 -> do
|
||||
len <- getULEB128 32
|
||||
init <- getLazyByteString len
|
||||
return $ DataSegment PassiveData init
|
||||
0x02 -> do
|
||||
memIdx <- getULEB128 32
|
||||
offset <- getExpression
|
||||
len <- getULEB128 32
|
||||
init <- getLazyByteString len
|
||||
return $ DataSegment (ActiveData memIdx offset) init
|
||||
byte -> fail $ "unknown data segment type: " ++ show byte
|
||||
memIdx <- getULEB128 32
|
||||
offset <- getExpression
|
||||
len <- getULEB128 32
|
||||
init <- getLazyByteString len
|
||||
return $ DataSegment memIdx offset init
|
||||
|
||||
instance Serialize Module where
|
||||
put mod = do
|
||||
|
||||
@@ -197,7 +197,7 @@ select pred a b = select' (produce pred) (produce a) (produce b)
|
||||
a
|
||||
res <- b
|
||||
pred
|
||||
appendExpr [Select Nothing]
|
||||
appendExpr [Select]
|
||||
return res
|
||||
|
||||
iBinOp :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => IBinOp -> a -> b -> GenFun (OutType a)
|
||||
@@ -643,10 +643,10 @@ store32 addr val offset align = do
|
||||
appendExpr [I64Store32 $ MemArg (fromIntegral offset) (fromIntegral align)]
|
||||
|
||||
memorySize :: GenFun (Proxy I32)
|
||||
memorySize = appendExpr [MemorySize] >> return Proxy
|
||||
memorySize = appendExpr [CurrentMemory] >> return Proxy
|
||||
|
||||
growMemory :: (Producer size, OutType size ~ Proxy I32) => size -> GenFun ()
|
||||
growMemory size = produce size >> appendExpr [MemoryGrow]
|
||||
growMemory size = produce size >> appendExpr [GrowMemory]
|
||||
|
||||
call :: (Returnable res) => Fn res -> [GenFun a] -> GenFun res
|
||||
call (Fn idx) args = sequence_ args >> appendExpr [Call idx] >> return returnableValue
|
||||
@@ -655,7 +655,7 @@ callIndirect :: (Producer index, OutType index ~ Proxy I32, Returnable res) => T
|
||||
callIndirect (TypeDef idx) index args = do
|
||||
sequence_ args
|
||||
produce index
|
||||
appendExpr [CallIndirect 0 idx]
|
||||
appendExpr [CallIndirect idx]
|
||||
return returnableValue
|
||||
|
||||
br :: Label t -> GenFun ()
|
||||
@@ -975,7 +975,7 @@ table min max = do
|
||||
dataSegment :: (Producer offset, OutType offset ~ Proxy I32) => offset -> LBS.ByteString -> GenMod ()
|
||||
dataSegment offset bytes =
|
||||
modify $ \(st@GenModState { target = m }) -> st {
|
||||
target = m { datas = datas m ++ [DataSegment (ActiveData 0 (genExpr 0 (produce offset))) bytes] }
|
||||
target = m { datas = datas m ++ [DataSegment 0 (genExpr 0 (produce offset)) bytes] }
|
||||
}
|
||||
|
||||
asWord32 :: Int32 -> Word32
|
||||
|
||||
+92
-1270
File diff suppressed because it is too large
Load Diff
+19
-27
@@ -5,8 +5,6 @@ module Language.Wasm.Lexer (
|
||||
Lexeme(..),
|
||||
Token(..),
|
||||
AlexPosn(..),
|
||||
FloatRep(..),
|
||||
NaN(..),
|
||||
scanner,
|
||||
asFloat,
|
||||
asDouble,
|
||||
@@ -24,8 +22,6 @@ import Data.List (isPrefixOf)
|
||||
import Text.Read (readEither)
|
||||
import Data.Bits
|
||||
import Numeric (showHex)
|
||||
import Control.DeepSeq (NFData)
|
||||
import GHC.Generics (Generic)
|
||||
|
||||
}
|
||||
|
||||
@@ -39,13 +35,13 @@ $alpha = [$lower $upper]
|
||||
$namepunct = [\! \# \$ \% \& \' \* \+ \- \. \/ \: \< \= \> \? \@ \∖ \^ \_ \` \| \~]
|
||||
$idchar = [$digit $alpha $namepunct]
|
||||
$space = [\ \x09 \x0A \x0D]
|
||||
$linechar = [^ \x09 \x0A \x0D]
|
||||
$linechar = [^ \x09]
|
||||
$sign = [\+ \-]
|
||||
$doublequote = \"
|
||||
|
||||
@keyword = $lower $idchar*
|
||||
@reserved = $idchar+
|
||||
@linecomment = ";;" $linechar* [\x0A \x0D]
|
||||
@linecomment = ";;" $linechar* \x0A
|
||||
@startblockcomment = "(;"
|
||||
@endblockcomment = ";)"
|
||||
@num = $digit (\_? $digit+)*
|
||||
@@ -80,10 +76,10 @@ tokens :-
|
||||
<0> @id { tokenStr TId }
|
||||
<0> "(" { constToken TOpenBracket }
|
||||
<0> ")" { constToken TCloseBracket }
|
||||
<0> $sign? @hexfloat { parseHexFloat }
|
||||
<0> $sign? @num { parseDecimalSignedInt }
|
||||
<0> $sign? "0x" @hexnum { parseHexalSignedInt }
|
||||
<0> $sign? @float { parseDecFloat }
|
||||
<0> $sign? @hexfloat { parseHexFloat }
|
||||
<0, blockComment> @startblockcomment { startBlockComment }
|
||||
<blockComment> [.\n] ;
|
||||
<blockComment> @endblockcomment { endBlockComment }
|
||||
@@ -119,22 +115,22 @@ minusNaN = negate nan
|
||||
inf = infinity
|
||||
minusInf = -infinity
|
||||
|
||||
parseSign :: (Num a) => LBS.ByteString -> ((a -> a), Int64, Maybe Bool)
|
||||
parseSign :: (Num a) => LBS.ByteString -> ((a -> a), Int64)
|
||||
parseSign str =
|
||||
let Just (ch, _) = LBSUtf8.decode str in
|
||||
case ch of
|
||||
'-' -> (negate, 1, Just True)
|
||||
'+' -> (abs, 1, Just False)
|
||||
otherwise -> (abs, 0, Nothing)
|
||||
'-' -> (negate, 1)
|
||||
'+' -> (abs, 1)
|
||||
otherwise -> (abs, 0)
|
||||
|
||||
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Integer -> Integer), Int64, Maybe Bool) #-}
|
||||
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Double -> Double), Int64, Maybe Bool) #-}
|
||||
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Integer -> Integer), Int64) #-}
|
||||
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Double -> Double), Int64) #-}
|
||||
|
||||
parseHexalSignedInt :: AlexAction Lexeme
|
||||
parseHexalSignedInt = token $ \(pos, _, s, _) len ->
|
||||
let (sign, slen, nat) = parseSign s in
|
||||
let (sign, slen) = parseSign s in
|
||||
let num = readHexFromPrefix (len - 2 - slen) $ LBSUtf8.drop (2 + slen) s in
|
||||
Lexeme (Just pos) $ TIntLit nat $ sign num
|
||||
Lexeme (Just pos) $ TIntLit $ sign num
|
||||
|
||||
parseNanSigned :: AlexAction Lexeme
|
||||
parseNanSigned = token $ \(pos, _, s, _) len ->
|
||||
@@ -148,9 +144,9 @@ parseNanSigned = token $ \(pos, _, s, _) len ->
|
||||
|
||||
parseDecimalSignedInt :: AlexAction Lexeme
|
||||
parseDecimalSignedInt = token $ \(pos, _, s, _) len ->
|
||||
let (sign, slen, nat) = parseSign s in
|
||||
let (sign, slen) = parseSign s in
|
||||
let num = readDecFromPrefix (len - slen) $ LBSUtf8.drop slen s in
|
||||
Lexeme (Just pos) $ TIntLit nat $ sign num
|
||||
Lexeme (Just pos) $ TIntLit $ sign num
|
||||
|
||||
parseDecFloat :: AlexAction Lexeme
|
||||
parseDecFloat = token $ \(pos, _, s, _) len ->
|
||||
@@ -226,13 +222,11 @@ readHexFloat toFloat sz expLimit manitisaSize str = do
|
||||
then ([True], 0, exp' + 1)
|
||||
else (rounded, 1, exp')
|
||||
else (rounded, 0, exp')
|
||||
e <- if exp'' > expLimit then Left "const out of range"
|
||||
else if exp'' < (negate $ expLimit + manitisaSize) then return $ negate $ expLimit + manitisaSize + 1
|
||||
else return exp''
|
||||
if e >= (negate $ expLimit - 1)
|
||||
then return $ toFloat $ sign .|. ((fromIntegral $ e + expLimit) `shiftL` manitisaSize) .|. ((fromBits (tail bits') + a) `shiftL` (manitisaSize + 1 - length bits'))
|
||||
if exp'' > expLimit || exp'' < (negate $ expLimit + manitisaSize) then Left "constant out of range" else return ()
|
||||
if exp'' >= (negate $ expLimit - 1)
|
||||
then return $ toFloat $ sign .|. ((fromIntegral $ exp'' + expLimit) `shiftL` manitisaSize) .|. ((fromBits (tail bits') + a) `shiftL` (manitisaSize + 1 - length bits'))
|
||||
else do
|
||||
let shift = expLimit + manitisaSize - length bits' - abs e
|
||||
let shift = expLimit + manitisaSize - length bits' - abs exp''
|
||||
if shift < 0
|
||||
then return $ toFloat sign
|
||||
else return $ toFloat $ sign .|. ((fromBits bits' + a) `shiftL` shift)
|
||||
@@ -301,9 +295,7 @@ endBlockComment _inp _len = do
|
||||
alexMonadScan
|
||||
|
||||
startStringLiteral :: AlexAction Lexeme
|
||||
startStringLiteral (_, prev, _, _) _len = do
|
||||
when (prev `notElem` "() \x09\x0A\x0D")
|
||||
$ alexError "string literal should start after space or parent character"
|
||||
startStringLiteral _inp _len = do
|
||||
alexSetStartCode stringLiteral
|
||||
setLexerStringFlag True
|
||||
alexMonadScan
|
||||
@@ -366,7 +358,7 @@ data NaN
|
||||
deriving (Show, Eq)
|
||||
|
||||
data Token = TKeyword LBS.ByteString
|
||||
| TIntLit {- Natural -} (Maybe Bool) Integer
|
||||
| TIntLit Integer
|
||||
| TFloatLit FloatRep
|
||||
| TStringLit LBS.ByteString
|
||||
| TId LBS.ByteString
|
||||
|
||||
+479
-1443
File diff suppressed because it is too large
Load Diff
+19
-62
@@ -11,10 +11,8 @@ import qualified Data.Text.Lazy.Encoding as TLEncoding
|
||||
import qualified Control.Monad.State as State
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Numeric.IEEE (identicalIEEE)
|
||||
import qualified Data.Primitive.ByteArray as ByteArray
|
||||
import qualified Control.DeepSeq as DeepSeq
|
||||
import Data.Maybe (fromJust, isNothing)
|
||||
import Debug.Trace (trace)
|
||||
|
||||
import Language.Wasm.Parser (
|
||||
Ident(..),
|
||||
@@ -31,9 +29,6 @@ import qualified Language.Wasm.Structure as Struct
|
||||
import qualified Language.Wasm.Parser as Parser
|
||||
import qualified Language.Wasm.Lexer as Lexer
|
||||
import qualified Language.Wasm.Binary as Binary
|
||||
import Language.Wasm.FloatUtils (floatToWord, wordToFloat, doubleToWord, wordToDouble)
|
||||
import Numeric.IEEE (nan)
|
||||
import Data.Bits ((.&.))
|
||||
|
||||
type OnAssertFail = String -> Assertion -> IO ()
|
||||
|
||||
@@ -60,7 +55,6 @@ runScript onAssertFail script = do
|
||||
(st, inst) <- Interpreter.makeHostModule Interpreter.emptyStore [
|
||||
("print", hostPrint []),
|
||||
("print_i32", hostPrint [Struct.I32]),
|
||||
("print_i64", hostPrint [Struct.I64]),
|
||||
("print_i32_f32", hostPrint [Struct.I32, Struct.F32]),
|
||||
("print_f64_f64", hostPrint [Struct.F64, Struct.F64]),
|
||||
("print_f32", hostPrint [Struct.F32]),
|
||||
@@ -78,8 +72,8 @@ runScript onAssertFail script = do
|
||||
hostGlobals = do
|
||||
let globI32 = Interpreter.makeConstGlobal $ Interpreter.VI32 666
|
||||
let globI64 = Interpreter.makeConstGlobal $ Interpreter.VI64 666
|
||||
let globF32 = Interpreter.makeConstGlobal $ Interpreter.VF32 666.6
|
||||
let globF64 = Interpreter.makeConstGlobal $ Interpreter.VF64 666.6
|
||||
let globF32 = Interpreter.makeConstGlobal $ Interpreter.VF32 666
|
||||
let globF64 = Interpreter.makeConstGlobal $ Interpreter.VF64 666
|
||||
return (
|
||||
Interpreter.HostGlobal globI32,
|
||||
Interpreter.HostGlobal globI64,
|
||||
@@ -123,22 +117,12 @@ runScript onAssertFail script = do
|
||||
getModule st (Just (Ident i)) = Map.lookup i (modules st)
|
||||
getModule st Nothing = lastModule st
|
||||
|
||||
asArg :: Parser.ValuePattern -> Interpreter.Value
|
||||
asArg (Parser.ExactValue (Struct.I32Const v)) = Interpreter.VI32 v
|
||||
asArg (Parser.ExactValue (Struct.F32Const v)) = Interpreter.VF32 v
|
||||
asArg (Parser.ExactValue (Struct.I64Const v)) = Interpreter.VI64 v
|
||||
asArg (Parser.ExactValue (Struct.F64Const v)) = Interpreter.VF64 v
|
||||
asArg (Parser.ExactValue (Struct.V128Const v)) = Interpreter.VV128 v
|
||||
asArg (Parser.ExactValue (Struct.RefNull Struct.FuncRef)) = Interpreter.RF Nothing
|
||||
asArg (Parser.ExactValue (Struct.RefNull Struct.ExternRef))= Interpreter.RE Nothing
|
||||
asArg (Parser.ExactValue (Struct.RefExtern v)) = Interpreter.RE (Just v)
|
||||
asArg expr = error $ "Only const instructions supported as arguments for actions: " ++ show expr
|
||||
|
||||
showArg :: Parser.ValuePattern -> String
|
||||
showArg v@(Parser.ExactValue _) = show $ asArg v
|
||||
showArg Parser.CanonicalNan = "nan:canonical"
|
||||
showArg Parser.ArithmeticNan = "nan:arithmetic"
|
||||
showArg (Parser.VectorPat _ pat) = show $ showArg <$> pat
|
||||
asArg :: Struct.Expression -> Interpreter.Value
|
||||
asArg [Struct.I32Const v] = Interpreter.VI32 v
|
||||
asArg [Struct.F32Const v] = Interpreter.VF32 v
|
||||
asArg [Struct.I64Const v] = Interpreter.VI64 v
|
||||
asArg [Struct.F64Const v] = Interpreter.VF64 v
|
||||
asArg _ = error "Only const instructions supported as arguments for actions"
|
||||
|
||||
runAction :: ScriptState -> Action -> IO (Maybe [Interpreter.Value])
|
||||
runAction st (Invoke ident name args) = do
|
||||
@@ -153,31 +137,10 @@ runScript onAssertFail script = do
|
||||
isValueEqual :: Interpreter.Value -> Interpreter.Value -> Bool
|
||||
isValueEqual (Interpreter.VI32 v1) (Interpreter.VI32 v2) = v1 == v2
|
||||
isValueEqual (Interpreter.VI64 v1) (Interpreter.VI64 v2) = v1 == v2
|
||||
isValueEqual (Interpreter.VF32 v1) (Interpreter.VF32 v2) = identicalIEEE v1 v2
|
||||
isValueEqual (Interpreter.VF64 v1) (Interpreter.VF64 v2) = identicalIEEE v1 v2
|
||||
isValueEqual (Interpreter.VV128 a) (Interpreter.VV128 b) = ByteArray.compareByteArrays a 0 b 0 16 == EQ
|
||||
isValueEqual (Interpreter.RF f1) (Interpreter.RF f2) = f1 == f2
|
||||
isValueEqual (Interpreter.RE e1) (Interpreter.RE e2) = e1 == e2
|
||||
isValueEqual (Interpreter.VF32 v1) (Interpreter.VF32 v2) = (isNaN v1 && isNaN v2) || identicalIEEE v1 v2
|
||||
isValueEqual (Interpreter.VF64 v1) (Interpreter.VF64 v2) = (isNaN v1 && isNaN v2) || identicalIEEE v1 v2
|
||||
isValueEqual _ _ = False
|
||||
|
||||
isValueMatch :: Interpreter.Value -> Parser.ValuePattern -> Bool
|
||||
isValueMatch val v@(Parser.ExactValue _) = isValueEqual val $ asArg v
|
||||
isValueMatch (Interpreter.VF32 v) Parser.CanonicalNan = identicalIEEE v nan || identicalIEEE v (abs nan)
|
||||
isValueMatch (Interpreter.VF32 v) Parser.ArithmeticNan =
|
||||
let posNan = 0x7F800000 in
|
||||
floatToWord v .&. posNan == posNan
|
||||
isValueMatch (Interpreter.VF64 v) Parser.CanonicalNan = identicalIEEE v nan || identicalIEEE v (abs nan)
|
||||
isValueMatch (Interpreter.VF64 v) Parser.ArithmeticNan =
|
||||
let posNan = 0x7FF0000000000000 in
|
||||
doubleToWord v .&. posNan == posNan
|
||||
isValueMatch (Interpreter.VV128 v) (Parser.VectorPat Struct.F32x4 pat) =
|
||||
let vals = Interpreter.VF32 . wordToFloat . ByteArray.indexByteArray v <$> [0..3] in
|
||||
and $ zipWith isValueMatch vals pat
|
||||
isValueMatch (Interpreter.VV128 v) (Parser.VectorPat Struct.F64x2 pat) =
|
||||
let vals = Interpreter.VF64 . wordToDouble . ByteArray.indexByteArray v <$> [0, 1] in
|
||||
and $ zipWith isValueMatch vals pat
|
||||
isValueMatch _ _ = False
|
||||
|
||||
isNaNReturned :: Action -> Assertion -> AssertM ()
|
||||
isNaNReturned action assert = do
|
||||
result <- runActionInAssert action
|
||||
@@ -198,7 +161,7 @@ runScript onAssertFail script = do
|
||||
let Right m = Lexer.scanner (TLEncoding.encodeUtf8 textRep) >>= Parser.parseModule in
|
||||
(ident, m)
|
||||
buildModule (BinaryModDef ident binaryRep) =
|
||||
let Right m = Binary.decodeModuleLazy binaryRep in
|
||||
let Right m = Binary.decodeModuleLazy binaryRep in
|
||||
(ident, m)
|
||||
|
||||
checkModuleInvalid :: Struct.Module -> IO ()
|
||||
@@ -206,16 +169,15 @@ runScript onAssertFail script = do
|
||||
|
||||
getFailureString :: Validate.ValidationError -> [TL.Text]
|
||||
getFailureString (Validate.TypeMismatch _ _) = ["type mismatch"]
|
||||
getFailureString (Validate.RefTypeMismatch _ _) = ["type mismatch"]
|
||||
getFailureString Validate.ResultTypeDoesntMatch = ["type mismatch"]
|
||||
getFailureString Validate.MoreThanOneMemory = ["multiple memories"]
|
||||
getFailureString Validate.MoreThanOneTable = ["multiple tables"]
|
||||
getFailureString (Validate.LocalIndexOutOfRange idx) = ["unknown local", "unknown local " <> TL.pack (show idx)]
|
||||
getFailureString (Validate.MemoryIndexOutOfRange idx) = ["unknown memory", "unknown memory " <> TL.pack (show idx)]
|
||||
getFailureString (Validate.TableIndexOutOfRange idx) = ["unknown table", "unknown table " <> TL.pack (show idx)]
|
||||
getFailureString (Validate.FunctionIndexOutOfRange idx) = ["unknown function", "unknown function " <> TL.pack (show idx)]
|
||||
getFailureString Validate.FunctionIndexOutOfRange = ["unknown function", "unknown function 0"]
|
||||
getFailureString (Validate.GlobalIndexOutOfRange idx) = ["unknown global", "unknown global " <> TL.pack (show idx)]
|
||||
getFailureString Validate.LabelIndexOutOfRange = ["unknown label"]
|
||||
getFailureString Validate.LaneIndexOutOfRange = ["invalid lane index"]
|
||||
getFailureString Validate.TypeIndexOutOfRange = ["unknown type"]
|
||||
getFailureString Validate.MinMoreThanMaxInMemoryLimit = ["size minimum must not be greater than maximum"]
|
||||
getFailureString Validate.MemoryLimitExceeded = ["memory size must be at most 65536 pages (4GiB)"]
|
||||
@@ -226,10 +188,7 @@ runScript onAssertFail script = do
|
||||
getFailureString Validate.GlobalIsImmutable = ["global is immutable"]
|
||||
getFailureString Validate.InvalidStartFunctionType = ["start function"]
|
||||
getFailureString Validate.InvalidTableType = ["size minimum must not be greater than maximum"]
|
||||
getFailureString (Validate.ElemIndexOutOfRange idx) = ["unknown elem segment " <> TL.pack (show idx)]
|
||||
getFailureString (Validate.DataIndexOutOfRange idx) = ["unknown data segment", "unknown data segment " <> TL.pack (show idx)]
|
||||
getFailureString (Validate.UndeclaredFunctionRef _) = ["undeclared function reference"]
|
||||
getFailureString r = [TL.concat ["not implemented ", TL.pack $ show r]]
|
||||
getFailureString r = [TL.concat ["not implemented ", (TL.pack $ show r)]]
|
||||
|
||||
printFailedAssert :: String -> Assertion -> AssertM ()
|
||||
printFailedAssert msg assert = do
|
||||
@@ -247,10 +206,10 @@ runScript onAssertFail script = do
|
||||
result <- runActionInAssert action
|
||||
case result of
|
||||
Just result -> do
|
||||
if length result == length expected && (all id $ zipWith isValueMatch result expected)
|
||||
if length result == length expected && (all id $ zipWith isValueEqual result (map asArg expected))
|
||||
then return ()
|
||||
else printFailedAssert ("Expected " ++ show (map showArg expected) ++ ", but action returned " ++ show result) assert
|
||||
Nothing -> printFailedAssert ("Expected " ++ show (map showArg expected) ++ ", but action returned Trap") assert
|
||||
else printFailedAssert ("Expected " ++ show (map asArg expected) ++ ", but action returned " ++ show result) assert
|
||||
Nothing -> printFailedAssert ("Expected " ++ show (map asArg expected) ++ ", but action returned Trap") assert
|
||||
runAssert assert@(AssertReturnCanonicalNaN action) = isNaNReturned action assert
|
||||
runAssert assert@(AssertReturnArithmeticNaN action) = isNaNReturned action assert
|
||||
runAssert assert@(AssertInvalid moduleDef failureString) =
|
||||
@@ -294,14 +253,12 @@ runScript onAssertFail script = do
|
||||
let (_, m) = buildModule moduleDef in
|
||||
case Validate.validate m of
|
||||
Right m -> do
|
||||
(st, pos) <- State.get
|
||||
st <- fst <$> State.get
|
||||
(res, store') <- liftIO $ Interpreter.instantiate (store st) (buildImports st) m
|
||||
State.put (st { store = store' }, pos)
|
||||
case res of
|
||||
Left err | err == TL.unpack failureString -> return ()
|
||||
Left "Start function terminated with trap" ->
|
||||
State.modify $ \(st, pos) -> (st { store = store' }, pos)
|
||||
r -> printFailedAssert "Module linking should fail with trap during execution of a start function" assert
|
||||
_ -> printFailedAssert ("Module linking should fail with trap during execution of a start function") assert
|
||||
Left reason -> error $ "Module linking failed due to invalid module with reason: " ++ show reason
|
||||
runAssert assert@(AssertExhaustion action failureString) = do
|
||||
result <- runActionInAssert action
|
||||
|
||||
+12
-106
@@ -4,10 +4,8 @@
|
||||
|
||||
module Language.Wasm.Structure (
|
||||
Module(..),
|
||||
DataMode(..),
|
||||
DataSegment(..),
|
||||
ElemSegment(..),
|
||||
ElemMode(..),
|
||||
StartFunction(..),
|
||||
Export(..),
|
||||
ExportDesc(..),
|
||||
@@ -33,7 +31,6 @@ module Language.Wasm.Structure (
|
||||
FuncType(..),
|
||||
ValueType(..),
|
||||
BlockType(..),
|
||||
SimdShape(..),
|
||||
ParamsType,
|
||||
ResultType,
|
||||
LocalsType,
|
||||
@@ -54,15 +51,12 @@ module Language.Wasm.Structure (
|
||||
|
||||
import Numeric.Natural (Natural)
|
||||
import Data.Word (Word32, Word64)
|
||||
import qualified Data.Primitive.ByteArray as ByteArray
|
||||
import qualified Data.ByteString.Lazy as LBS
|
||||
import qualified Data.Text.Lazy as TL
|
||||
import Control.DeepSeq (NFData)
|
||||
import GHC.Generics (Generic)
|
||||
|
||||
data SimdShape = I8x16 | I16x8 | I32x4 | I64x2 | F32x4 | F64x2 | I128x1 deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data BitSize = BS32 | BS64 | BS128 SimdShape deriving (Show, Eq, Generic, NFData)
|
||||
data BitSize = BS32 | BS64 deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data IUnOp =
|
||||
IClz
|
||||
@@ -71,27 +65,17 @@ data IUnOp =
|
||||
| IExtend8S
|
||||
| IExtend16S
|
||||
| IExtend32S
|
||||
| INot
|
||||
| IAbs
|
||||
| INeg
|
||||
| IExtAddPairwise {- Signed -} Bool
|
||||
deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data IBinOp =
|
||||
IAdd
|
||||
| ISub
|
||||
| IAddSatS
|
||||
| ISubSatS
|
||||
| IAddSatU
|
||||
| ISubSatU
|
||||
| IAvgrU
|
||||
| IMul
|
||||
| IDivU
|
||||
| IDivS
|
||||
| IRemU
|
||||
| IRemS
|
||||
| IAnd
|
||||
| IAndNot
|
||||
| IOr
|
||||
| IXor
|
||||
| IShl
|
||||
@@ -99,18 +83,13 @@ data IBinOp =
|
||||
| IShrS
|
||||
| IRotl
|
||||
| IRotr
|
||||
| IMinU
|
||||
| IMinS
|
||||
| IMaxU
|
||||
| IMaxS
|
||||
| IExtMul {- Signed -} Bool {- High -} Bool
|
||||
deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data IRelOp = IEq | INe | ILtU | ILtS | IGtU | IGtS | ILeU | ILeS | IGeU | IGeS deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data FUnOp = FAbs | FNeg | FCeil | FFloor | FTrunc | FNearest | FSqrt deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data FBinOp = FAdd | FSub | FMul | FDiv | FMin | FMax | FCopySign | FPMin | FPMax deriving (Show, Eq, Generic, NFData)
|
||||
data FBinOp = FAdd | FSub | FMul | FDiv | FMin | FMax | FCopySign deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data FRelOp = FEq | FNe | FLt | FGt | FLe | FGe deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
@@ -123,17 +102,12 @@ type LocalIndex = Natural
|
||||
type GlobalIndex = Natural
|
||||
type MemoryIndex = Natural
|
||||
type TableIndex = Natural
|
||||
type DataIndex = Natural
|
||||
type ElemIndex = Natural
|
||||
|
||||
data ValueType =
|
||||
I32
|
||||
| I64
|
||||
| F32
|
||||
| F64
|
||||
| V128
|
||||
| Func
|
||||
| Extern
|
||||
deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
type ResultType = [ValueType]
|
||||
@@ -159,15 +133,10 @@ data Instruction index =
|
||||
| BrTable [index] index
|
||||
| Return
|
||||
| Call index
|
||||
| CallIndirect index index
|
||||
-- Reference instructions
|
||||
| RefNull ElemType
|
||||
| RefIsNull
|
||||
| RefFunc index
|
||||
| RefExtern Natural
|
||||
| CallIndirect index
|
||||
-- Parametric instructions
|
||||
| Drop
|
||||
| Select (Maybe [ValueType])
|
||||
| Select
|
||||
-- Variable instructions
|
||||
| GetLocal index
|
||||
| SetLocal index
|
||||
@@ -179,23 +148,6 @@ data Instruction index =
|
||||
| I64Load MemArg
|
||||
| F32Load MemArg
|
||||
| F64Load MemArg
|
||||
| V128Load MemArg
|
||||
| V128Load8Lane MemArg Natural
|
||||
| V128Load16Lane MemArg Natural
|
||||
| V128Load32Lane MemArg Natural
|
||||
| V128Load64Lane MemArg Natural
|
||||
| V128Load8Splat MemArg
|
||||
| V128Load16Splat MemArg
|
||||
| V128Load32Splat MemArg
|
||||
| V128Load64Splat MemArg
|
||||
| V128Load32Zero MemArg
|
||||
| V128Load64Zero MemArg
|
||||
| V128Load8x8S MemArg
|
||||
| V128Load8x8U MemArg
|
||||
| V128Load16x4S MemArg
|
||||
| V128Load16x4U MemArg
|
||||
| V128Load32x2S MemArg
|
||||
| V128Load32x2U MemArg
|
||||
| I32Load8S MemArg
|
||||
| I32Load8U MemArg
|
||||
| I32Load16S MemArg
|
||||
@@ -210,37 +162,18 @@ data Instruction index =
|
||||
| I64Store MemArg
|
||||
| F32Store MemArg
|
||||
| F64Store MemArg
|
||||
| V128Store MemArg
|
||||
| V128Store8Lane MemArg Natural
|
||||
| V128Store16Lane MemArg Natural
|
||||
| V128Store32Lane MemArg Natural
|
||||
| V128Store64Lane MemArg Natural
|
||||
| I32Store8 MemArg
|
||||
| I32Store16 MemArg
|
||||
| I64Store8 MemArg
|
||||
| I64Store16 MemArg
|
||||
| I64Store32 MemArg
|
||||
| MemorySize
|
||||
| MemoryGrow
|
||||
| MemoryFill
|
||||
| MemoryCopy
|
||||
| MemoryInit DataIndex
|
||||
| DataDrop DataIndex
|
||||
-- Table instructions
|
||||
| TableInit TableIndex ElemIndex
|
||||
| TableGrow TableIndex
|
||||
| TableSize TableIndex
|
||||
| TableFill TableIndex
|
||||
| TableGet TableIndex
|
||||
| TableSet TableIndex
|
||||
| TableCopy TableIndex TableIndex
|
||||
| ElemDrop ElemIndex
|
||||
| CurrentMemory
|
||||
| GrowMemory
|
||||
-- Numeric instructions
|
||||
| I32Const Word32
|
||||
| I64Const Word64
|
||||
| F32Const Float
|
||||
| F64Const Double
|
||||
| V128Const ByteArray.ByteArray
|
||||
| IUnOp BitSize IUnOp
|
||||
| IBinOp BitSize IBinOp
|
||||
| I32Eqz
|
||||
@@ -262,23 +195,6 @@ data Instruction index =
|
||||
| F64PromoteF32
|
||||
| IReinterpretF BitSize
|
||||
| FReinterpretI BitSize
|
||||
-- Vector instructions
|
||||
| V128Splat SimdShape
|
||||
| V128ExtractLane SimdShape index {- signed -} Bool
|
||||
| V128ReplaceLane SimdShape index
|
||||
| V128AllTrue SimdShape
|
||||
| V128BitMask SimdShape
|
||||
| V128AnyTrue
|
||||
| V128BitSelect
|
||||
| I8x16Swizzle
|
||||
| I8x16Shuffle [Int]
|
||||
| V128Narrow SimdShape SimdShape {- signed -} Bool
|
||||
| F64x2PromoteLowF32x4
|
||||
| F32x4DemoteF64x2Zero
|
||||
| V128IExtend SimdShape SimdShape {- high -} Bool {- signed -} Bool
|
||||
| I32x4TruncSatF {- signed -} Bool {- Float Size -} BitSize
|
||||
| I32x4DotI16x8S
|
||||
| I16x8Q15MulrSatS
|
||||
deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
type Expression = [Instruction Natural]
|
||||
@@ -291,7 +207,7 @@ data Function = Function {
|
||||
|
||||
data Limit = Limit Natural (Maybe Natural) deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data ElemType = FuncRef | ExternRef deriving (Show, Eq, Generic, NFData)
|
||||
data ElemType = FuncRef deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data TableType = TableType Limit ElemType deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
@@ -306,25 +222,15 @@ data Global = Global {
|
||||
initializer :: Expression
|
||||
} deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data ElemMode =
|
||||
Passive
|
||||
| Active TableIndex Expression
|
||||
| Declarative
|
||||
deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data ElemSegment = ElemSegment {
|
||||
elemType :: ElemType,
|
||||
mode :: ElemMode,
|
||||
elements :: [Expression]
|
||||
tableIndex :: TableIndex,
|
||||
offset :: Expression,
|
||||
funcIndexes :: [FuncIndex]
|
||||
} deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data DataMode =
|
||||
PassiveData
|
||||
| ActiveData MemoryIndex Expression
|
||||
deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
data DataSegment = DataSegment {
|
||||
dataMode :: DataMode,
|
||||
memIndex :: MemoryIndex,
|
||||
offset :: Expression,
|
||||
chunk :: LBS.ByteString
|
||||
} deriving (Show, Eq, Generic, NFData)
|
||||
|
||||
|
||||
+159
-458
@@ -15,11 +15,11 @@ import Language.Wasm.Structure
|
||||
import qualified Data.Set as Set
|
||||
import Data.List (foldl')
|
||||
import qualified Data.Text.Lazy as TL
|
||||
import Data.Maybe (fromMaybe, catMaybes)
|
||||
import Data.Maybe (fromMaybe, maybeToList, catMaybes)
|
||||
import Numeric.Natural (Natural)
|
||||
import Prelude hiding ((<>))
|
||||
|
||||
import Control.Monad (foldM, forM_, when, unless)
|
||||
import Control.Monad (foldM)
|
||||
import Control.Monad.Reader (ReaderT, runReaderT, withReaderT, ask)
|
||||
import Control.Monad.Except (Except, runExcept, throwError)
|
||||
|
||||
@@ -32,24 +32,20 @@ data ValidationError =
|
||||
| MemoryLimitExceeded
|
||||
| AlignmentOverflow
|
||||
| MoreThanOneMemory
|
||||
| FunctionIndexOutOfRange Natural
|
||||
| MoreThanOneTable
|
||||
| FunctionIndexOutOfRange
|
||||
| TableIndexOutOfRange Natural
|
||||
| MemoryIndexOutOfRange Natural
|
||||
| LocalIndexOutOfRange Natural
|
||||
| GlobalIndexOutOfRange Natural
|
||||
| ElemIndexOutOfRange Natural
|
||||
| DataIndexOutOfRange Natural
|
||||
| LabelIndexOutOfRange
|
||||
| LaneIndexOutOfRange
|
||||
| TypeIndexOutOfRange
|
||||
| ResultTypeDoesntMatch
|
||||
| TypeMismatch { actual :: Arrow, expected :: Arrow }
|
||||
| RefTypeMismatch ElemType ElemType
|
||||
| InvalidResultArity
|
||||
| InvalidConstantExpr
|
||||
| InvalidStartFunctionType
|
||||
| GlobalIsImmutable
|
||||
| UndeclaredFunctionRef Natural
|
||||
deriving (Show, Eq)
|
||||
|
||||
type ValidationResult = Either ValidationError ()
|
||||
@@ -66,14 +62,13 @@ instance Monoid ValidationResult where
|
||||
|
||||
isValid :: ValidationResult -> Bool
|
||||
isValid (Right ()) = True
|
||||
isValid (Left reason) = False
|
||||
isValid (Left reason) = Debug.trace ("Module mismatched with reason " ++ show reason) $ False
|
||||
|
||||
type Validator = Module -> ValidationResult
|
||||
|
||||
data VType =
|
||||
Val ValueType
|
||||
| Var
|
||||
| NonRefVar
|
||||
| Any
|
||||
deriving (Show, Eq)
|
||||
|
||||
@@ -108,11 +103,6 @@ asArrow (FuncType params results) = Arrow (map Val params) (map Val $ reverse re
|
||||
isArrowMatch :: Arrow -> Arrow -> Bool
|
||||
isArrowMatch (f `Arrow` t) ( f' `Arrow` t') = isEndMatch f f' && isEndMatch t t'
|
||||
where
|
||||
isRef :: VType -> Bool
|
||||
isRef (Val Func) = True
|
||||
isRef (Val Extern) = True
|
||||
isRef _ = False
|
||||
|
||||
isEndMatch :: End -> End -> Bool
|
||||
isEndMatch (Any:l) (Any:r) =
|
||||
let (leftTail, rightTail) = unzip $ zip (takeWhile (/= Any) $ reverse l) (takeWhile (/= Any) $ reverse r) in
|
||||
@@ -129,12 +119,6 @@ isArrowMatch (f `Arrow` t) ( f' `Arrow` t') = isEndMatch f f' && isEndMatch t t'
|
||||
isEndMatch (x:l) (Var:r) =
|
||||
let subst = replace Var x in
|
||||
isEndMatch (subst l) (subst r)
|
||||
isEndMatch (NonRefVar:l) (x:r) =
|
||||
let subst = replace NonRefVar x in
|
||||
isEndMatch (subst l) (subst r)
|
||||
isEndMatch (x:l) (NonRefVar:r) =
|
||||
let subst = replace NonRefVar x in
|
||||
isEndMatch (subst l) (subst r)
|
||||
isEndMatch (Val v:l) (Val v':r) = v == v' && isEndMatch l r
|
||||
isEndMatch [] [] = True
|
||||
isEndMatch _ _ = False
|
||||
@@ -142,16 +126,13 @@ isArrowMatch (f `Arrow` t) ( f' `Arrow` t') = isEndMatch f f' && isEndMatch t t'
|
||||
data Ctx = Ctx {
|
||||
types :: [FuncType],
|
||||
funcs :: [FuncType],
|
||||
tableTypes :: [TableType],
|
||||
elems :: [ElemType],
|
||||
datas :: [DataMode],
|
||||
tables :: [TableType],
|
||||
mems :: [Limit],
|
||||
globals :: [GlobalType],
|
||||
locals :: [ValueType],
|
||||
labels :: [[ValueType]],
|
||||
returns :: [ValueType],
|
||||
importedGlobals :: Natural,
|
||||
refs :: Set.Set Natural
|
||||
importedGlobals :: Natural
|
||||
} deriving (Show, Eq)
|
||||
|
||||
type Checker = ReaderT Ctx (Except ValidationError)
|
||||
@@ -193,13 +174,10 @@ getLabel lbl = do
|
||||
withLabel :: [ValueType] -> Checker a -> Checker a
|
||||
withLabel result = withReaderT (\ctx -> ctx { labels = result : labels ctx })
|
||||
|
||||
isMemArgValid :: Natural -> MemArg -> Checker ()
|
||||
isMemArgValid sizeInBytes MemArg { align } =
|
||||
if 2 ^ align <= sizeInBytes
|
||||
then return ()
|
||||
else throwError AlignmentOverflow
|
||||
isMemArgValid :: Int -> MemArg -> Checker ()
|
||||
isMemArgValid sizeInBytes MemArg { align } = if 2 ^ align <= sizeInBytes then return () else throwError AlignmentOverflow
|
||||
|
||||
checkMemoryInstr :: Natural -> MemArg -> Checker ()
|
||||
checkMemoryInstr :: Int -> MemArg -> Checker ()
|
||||
checkMemoryInstr size memarg = do
|
||||
isMemArgValid size memarg
|
||||
Ctx { mems } <- ask
|
||||
@@ -219,28 +197,24 @@ getResultType (TypeIndex typeIdx) = do
|
||||
Ctx { types } <- ask
|
||||
maybeToEither TypeIndexOutOfRange $ results <$> types !? typeIdx
|
||||
|
||||
elemTypeToRefType :: ElemType -> ValueType
|
||||
elemTypeToRefType FuncRef = Func
|
||||
elemTypeToRefType ExternRef = Extern
|
||||
|
||||
getInstrType :: [VType] -> Instruction Natural -> Checker Arrow
|
||||
getInstrType _ Unreachable = return $ Any ==> Any
|
||||
getInstrType _ Nop = return $ empty ==> empty
|
||||
getInstrType _ Block { blockType, body } = do
|
||||
getInstrType :: Instruction Natural -> Checker Arrow
|
||||
getInstrType Unreachable = return $ Any ==> Any
|
||||
getInstrType Nop = return $ empty ==> empty
|
||||
getInstrType Block { blockType, body } = do
|
||||
bt@(Arrow from _) <- getBlockType blockType
|
||||
resultType <- getResultType blockType
|
||||
t <- withLabel resultType $ getExpressionTypeWithInput from body
|
||||
if isArrowMatch t bt
|
||||
then return bt
|
||||
else throwError $ TypeMismatch t bt
|
||||
getInstrType _ Loop { blockType, body } = do
|
||||
getInstrType Loop { blockType, body } = do
|
||||
bt@(Arrow from _) <- getBlockType blockType
|
||||
resultType <- getResultType blockType
|
||||
t <- withLabel (map (\(Val v) -> v) from) $ getExpressionTypeWithInput from body
|
||||
if isArrowMatch t bt
|
||||
then return bt
|
||||
else throwError $ TypeMismatch t bt
|
||||
getInstrType _ If { blockType, true, false } = do
|
||||
getInstrType If { blockType, true, false } = do
|
||||
bt@(Arrow from _) <- getBlockType blockType
|
||||
resultType <- getResultType blockType
|
||||
l <- withLabel resultType $ getExpressionTypeWithInput from true
|
||||
@@ -253,411 +227,185 @@ getInstrType _ If { blockType, true, false } = do
|
||||
else (throwError $ TypeMismatch r bt)
|
||||
)
|
||||
else throwError $ TypeMismatch l bt
|
||||
getInstrType _ (Br lbl) = do
|
||||
getInstrType (Br lbl) = do
|
||||
r <- map Val <$> getLabel lbl
|
||||
return $ (Any : r) ==> Any
|
||||
getInstrType _ (BrIf lbl) = do
|
||||
getInstrType (BrIf lbl) = do
|
||||
r <- map Val <$> getLabel lbl
|
||||
return $ (r ++ [Val I32]) ==> r
|
||||
getInstrType stack (BrTable lbls lbl) = do
|
||||
getInstrType (BrTable lbls lbl) = do
|
||||
r <- getLabel lbl
|
||||
let returns lbl = do
|
||||
args <- map Val <$> getLabel lbl
|
||||
res <- matchStack stack (Val I32 : reverse args) []
|
||||
return (args, res)
|
||||
alternatives <- mapM returns lbls
|
||||
(_, def) <- returns lbl
|
||||
if all (\(args, res) -> res == def && length args == length r) alternatives
|
||||
rs <- mapM getLabel lbls
|
||||
if all (== r) rs
|
||||
then return $ ([Any] ++ (map Val r) ++ [Val I32]) ==> Any
|
||||
else throwError ResultTypeDoesntMatch
|
||||
getInstrType _ Return = do
|
||||
getInstrType Return = do
|
||||
Ctx { returns } <- ask
|
||||
return $ (Any : (map Val returns)) ==> Any
|
||||
getInstrType _ (Call fun) = do
|
||||
getInstrType (Call fun) = do
|
||||
Ctx { funcs } <- ask
|
||||
maybeToEither (FunctionIndexOutOfRange fun) $ asArrow <$> funcs !? fun
|
||||
getInstrType _ (CallIndirect tableIdx sign) = do
|
||||
Ctx { types, tableTypes = tables } <- ask
|
||||
if length tables <= fromIntegral tableIdx
|
||||
then throwError (TableIndexOutOfRange tableIdx)
|
||||
maybeToEither FunctionIndexOutOfRange $ asArrow <$> funcs !? fun
|
||||
getInstrType (CallIndirect sign) = do
|
||||
Ctx { types, tables } <- ask
|
||||
if length tables < 1
|
||||
then throwError (TableIndexOutOfRange 0)
|
||||
else do
|
||||
let TableType _ elemType = tables !! fromIntegral tableIdx
|
||||
when (elemType /= FuncRef) $ throwError (RefTypeMismatch FuncRef ExternRef)
|
||||
Arrow from to <- maybeToEither TypeIndexOutOfRange $ asArrow <$> types !? sign
|
||||
return $ (from ++ [Val I32]) ==> to
|
||||
getInstrType _ Drop = do
|
||||
getInstrType Drop = do
|
||||
var <- freshVar
|
||||
return $ var ==> empty
|
||||
getInstrType _ (Select Nothing) = do
|
||||
var <- return NonRefVar
|
||||
return $ [var, var, Val I32] ==> var
|
||||
getInstrType _ (Select (Just vt)) =
|
||||
case vt of
|
||||
[t] -> return $ [t, t, I32] ==> t
|
||||
_ -> throwError InvalidResultArity
|
||||
getInstrType _ (RefNull elType) = do
|
||||
let t = case elType of { FuncRef -> Func; ExternRef -> Extern }
|
||||
return $ empty ==> Val t
|
||||
getInstrType _ RefIsNull = do
|
||||
getInstrType Select = do
|
||||
var <- freshVar
|
||||
return $ var ==> Val I32
|
||||
getInstrType _ (RefFunc funIdx) = do
|
||||
Ctx { funcs, refs } <- ask
|
||||
if fromIntegral funIdx < length funcs
|
||||
then do
|
||||
unless (Set.member funIdx refs) $
|
||||
throwError $ UndeclaredFunctionRef $ fromIntegral funIdx
|
||||
return $ empty ==> Val Func
|
||||
else throwError $ FunctionIndexOutOfRange $ fromIntegral funIdx
|
||||
getInstrType _ (GetLocal local) = do
|
||||
return $ [var, var, Val I32] ==> var
|
||||
getInstrType (GetLocal local) = do
|
||||
Ctx { locals } <- ask
|
||||
t <- maybeToEither (LocalIndexOutOfRange local) $ locals !? local
|
||||
return $ empty ==> Val t
|
||||
getInstrType _ (SetLocal local) = do
|
||||
getInstrType (SetLocal local) = do
|
||||
Ctx { locals } <- ask
|
||||
t <- maybeToEither (LocalIndexOutOfRange local) $ locals !? local
|
||||
return $ Val t ==> empty
|
||||
getInstrType _ (TeeLocal local) = do
|
||||
getInstrType (TeeLocal local) = do
|
||||
Ctx { locals } <- ask
|
||||
t <- maybeToEither (LocalIndexOutOfRange local) $ locals !? local
|
||||
return $ Val t ==> Val t
|
||||
getInstrType _ (GetGlobal global) = do
|
||||
getInstrType (GetGlobal global) = do
|
||||
Ctx { globals } <- ask
|
||||
t <- maybeToEither (GlobalIndexOutOfRange global) $ asType <$> globals !? global
|
||||
return $ empty ==> t
|
||||
getInstrType _ (SetGlobal global) = do
|
||||
getInstrType (SetGlobal global) = do
|
||||
Ctx { globals } <- ask
|
||||
t <- maybeToEither (GlobalIndexOutOfRange global) $ asType <$> globals !? global
|
||||
shouldBeMut $ globals !! fromIntegral global
|
||||
return $ t ==> empty
|
||||
getInstrType _ (I32Load memarg) = do
|
||||
getInstrType (I32Load memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ I32 ==> I32
|
||||
getInstrType _ (I64Load memarg) = do
|
||||
getInstrType (I64Load memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (F32Load memarg) = do
|
||||
getInstrType (F32Load memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ I32 ==> F32
|
||||
getInstrType _ (F64Load memarg) = do
|
||||
getInstrType (F64Load memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> F64
|
||||
getInstrType _ (V128Load memarg) = do
|
||||
checkMemoryInstr 16 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load8Lane memarg idx) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
when (idx >= 16) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> V128
|
||||
getInstrType _ (V128Load16Lane memarg idx) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
when (idx >= 8) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> V128
|
||||
getInstrType _ (V128Load32Lane memarg idx) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
when (idx >= 4) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> V128
|
||||
getInstrType _ (V128Load64Lane memarg idx) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
when (idx >= 2) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> V128
|
||||
getInstrType _ (V128Load8Splat memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load16Splat memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load32Splat memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load64Splat memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load32Zero memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load64Zero memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load8x8S memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load8x8U memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load16x4S memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load16x4U memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load32x2S memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (V128Load32x2U memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ I32 ==> V128
|
||||
getInstrType _ (I32Load8S memarg) = do
|
||||
getInstrType (I32Load8S memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ I32 ==> I32
|
||||
getInstrType _ (I32Load8U memarg) = do
|
||||
getInstrType (I32Load8U memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ I32 ==> I32
|
||||
getInstrType _ (I32Load16S memarg) = do
|
||||
getInstrType (I32Load16S memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ I32 ==> I32
|
||||
getInstrType _ (I32Load16U memarg) = do
|
||||
getInstrType (I32Load16U memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ I32 ==> I32
|
||||
getInstrType _ (I64Load8S memarg) = do
|
||||
getInstrType (I64Load8S memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (I64Load8U memarg) = do
|
||||
getInstrType (I64Load8U memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (I64Load16S memarg) = do
|
||||
getInstrType (I64Load16S memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (I64Load16U memarg) = do
|
||||
getInstrType (I64Load16U memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (I64Load32S memarg) = do
|
||||
getInstrType (I64Load32S memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (I64Load32U memarg) = do
|
||||
getInstrType (I64Load32U memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ I32 ==> I64
|
||||
getInstrType _ (I32Store memarg) = do
|
||||
getInstrType (I32Store memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ [I32, I32] ==> empty
|
||||
getInstrType _ (I64Store memarg) = do
|
||||
getInstrType (I64Store memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ [I32, I64] ==> empty
|
||||
getInstrType _ (F32Store memarg) = do
|
||||
getInstrType (F32Store memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ [I32, F32] ==> empty
|
||||
getInstrType _ (F64Store memarg) = do
|
||||
getInstrType (F64Store memarg) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
return $ [I32, F64] ==> empty
|
||||
getInstrType _ (V128Store memarg) = do
|
||||
checkMemoryInstr 16 memarg
|
||||
return $ [I32, V128] ==> empty
|
||||
getInstrType _ (V128Store8Lane memarg idx) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
when (idx >= 16) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> empty
|
||||
getInstrType _ (V128Store16Lane memarg idx) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
when (idx >= 8) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> empty
|
||||
getInstrType _ (V128Store32Lane memarg idx) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
when (idx >= 4) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> empty
|
||||
getInstrType _ (V128Store64Lane memarg idx) = do
|
||||
checkMemoryInstr 8 memarg
|
||||
when (idx >= 2) $ throwError LaneIndexOutOfRange
|
||||
return $ [I32, V128] ==> empty
|
||||
getInstrType _ (I32Store8 memarg) = do
|
||||
getInstrType (I32Store8 memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ [I32, I32] ==> empty
|
||||
getInstrType _ (I32Store16 memarg) = do
|
||||
getInstrType (I32Store16 memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ [I32, I32] ==> empty
|
||||
getInstrType _ (I64Store8 memarg) = do
|
||||
getInstrType (I64Store8 memarg) = do
|
||||
checkMemoryInstr 1 memarg
|
||||
return $ [I32, I64] ==> empty
|
||||
getInstrType _ (I64Store16 memarg) = do
|
||||
getInstrType (I64Store16 memarg) = do
|
||||
checkMemoryInstr 2 memarg
|
||||
return $ [I32, I64] ==> empty
|
||||
getInstrType _ (I64Store32 memarg) = do
|
||||
getInstrType (I64Store32 memarg) = do
|
||||
checkMemoryInstr 4 memarg
|
||||
return $ [I32, I64] ==> empty
|
||||
getInstrType _ MemorySize = do
|
||||
getInstrType CurrentMemory = do
|
||||
Ctx { mems } <- ask
|
||||
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||
return $ empty ==> I32
|
||||
getInstrType _ MemoryGrow = do
|
||||
Ctx { mems } <- ask
|
||||
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||
return $ I32 ==> I32
|
||||
getInstrType _ MemoryFill = do
|
||||
Ctx { mems } <- ask
|
||||
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||
return $ [I32, I32, I32] ==> empty
|
||||
getInstrType _ MemoryCopy = do
|
||||
Ctx { mems } <- ask
|
||||
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||
return $ [I32, I32, I32] ==> empty
|
||||
getInstrType _ (MemoryInit dataIdx) = do
|
||||
Ctx { mems, datas } <- ask
|
||||
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||
when (length datas <= fromIntegral dataIdx) $ throwError (DataIndexOutOfRange dataIdx)
|
||||
return $ [I32, I32, I32] ==> empty
|
||||
getInstrType _ (DataDrop dataIdx) = do
|
||||
Ctx { datas } <- ask
|
||||
when (length datas <= fromIntegral dataIdx) $ throwError (DataIndexOutOfRange dataIdx)
|
||||
return $ empty ==> empty
|
||||
getInstrType _ (TableInit tableIdx elemIdx) = do
|
||||
Ctx { tableTypes = tables, elems } <- ask
|
||||
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||
when (length elems <= fromIntegral elemIdx) $ throwError (ElemIndexOutOfRange elemIdx)
|
||||
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||
let elemType = elems !! fromIntegral elemIdx
|
||||
when (elemType /= tableType) $ throwError (RefTypeMismatch tableType elemType)
|
||||
return $ [I32, I32, I32] ==> empty
|
||||
getInstrType _ (TableCopy toIdx fromIdx) = do
|
||||
Ctx { tableTypes = tables } <- ask
|
||||
let (from, to) = (fromIntegral fromIdx, fromIntegral toIdx)
|
||||
when (length tables <= from) $ throwError (TableIndexOutOfRange fromIdx)
|
||||
when (length tables <= to) $ throwError (TableIndexOutOfRange toIdx)
|
||||
let TableType _ fromType = tables !! from
|
||||
let TableType _ toType = tables !! to
|
||||
when (fromType /= toType) $ throwError (RefTypeMismatch fromType toType)
|
||||
return $ [I32, I32, I32] ==> empty
|
||||
getInstrType _ (TableFill tableIdx) = do
|
||||
Ctx { tableTypes = tables } <- ask
|
||||
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||
return $ [I32, elemTypeToRefType tableType, I32] ==> empty
|
||||
getInstrType _ (TableSize tableIdx) = do
|
||||
Ctx { tableTypes = tables } <- ask
|
||||
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||
return $ empty ==> I32
|
||||
getInstrType _ (TableGrow tableIdx) = do
|
||||
Ctx { tableTypes = tables } <- ask
|
||||
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||
return $ [elemTypeToRefType tableType, I32] ==> I32
|
||||
getInstrType _ (TableGet tableIdx) = do
|
||||
Ctx { tableTypes = tables } <- ask
|
||||
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||
return $ I32 ==> (elemTypeToRefType tableType)
|
||||
getInstrType _ (TableSet tableIdx) = do
|
||||
Ctx { tableTypes = tables } <- ask
|
||||
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||
return $ [I32, elemTypeToRefType tableType] ==> empty
|
||||
getInstrType _ (ElemDrop elemIdx) = do
|
||||
Ctx { elems } <- ask
|
||||
when (length elems <= fromIntegral elemIdx) $ throwError (ElemIndexOutOfRange elemIdx)
|
||||
return $ empty ==> empty
|
||||
getInstrType _ (I32Const _) = return $ empty ==> I32
|
||||
getInstrType _ (I64Const _) = return $ empty ==> I64
|
||||
getInstrType _ (F32Const _) = return $ empty ==> F32
|
||||
getInstrType _ (F64Const _) = return $ empty ==> F64
|
||||
getInstrType _ (V128Const _) = return $ empty ==> V128
|
||||
getInstrType _ (IUnOp BS32 _) = return $ I32 ==> I32
|
||||
getInstrType _ (IUnOp BS64 _) = return $ I64 ==> I64
|
||||
getInstrType _ (IUnOp (BS128 _) _) = return $ V128 ==> V128
|
||||
getInstrType _ (IBinOp BS32 _) = return $ [I32, I32] ==> I32
|
||||
getInstrType _ (IBinOp BS64 _) = return $ [I64, I64] ==> I64
|
||||
getInstrType _ (IBinOp (BS128 _) op) | op == IShl || op == IShrS || op == IShrU =
|
||||
return $ [V128, I32] ==> V128
|
||||
getInstrType _ (IBinOp (BS128 _) _) = return $ [V128, V128] ==> V128
|
||||
getInstrType _ I32Eqz = return $ I32 ==> I32
|
||||
getInstrType _ I64Eqz = return $ I64 ==> I32
|
||||
getInstrType _ (IRelOp BS32 _) = return $ [I32, I32] ==> I32
|
||||
getInstrType _ (IRelOp BS64 _) = return $ [I64, I64] ==> I32
|
||||
getInstrType _ (IRelOp (BS128 _) _) = return $ [V128, V128] ==> V128
|
||||
getInstrType _ (FUnOp BS32 _) = return $ F32 ==> F32
|
||||
getInstrType _ (FUnOp BS64 _) = return $ F64 ==> F64
|
||||
getInstrType _ (FUnOp (BS128 _) _) = return $ V128 ==> V128
|
||||
getInstrType _ (FBinOp BS32 _) = return $ [F32, F32] ==> F32
|
||||
getInstrType _ (FBinOp BS64 _) = return $ [F64, F64] ==> F64
|
||||
getInstrType _ (FBinOp (BS128 _) _) = return $ [V128, V128] ==> V128
|
||||
getInstrType _ (FRelOp BS32 _) = return $ [F32, F32] ==> I32
|
||||
getInstrType _ (FRelOp BS64 _) = return $ [F64, F64] ==> I32
|
||||
getInstrType _ (FRelOp (BS128 _) _) = return $ [V128, V128] ==> V128
|
||||
getInstrType _ I32WrapI64 = return $ I64 ==> I32
|
||||
getInstrType _ (ITruncFU BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType _ (ITruncFU BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType _ (ITruncFU BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType _ (ITruncFU BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType _ (ITruncFS BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType _ (ITruncFS BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType _ (ITruncFS BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType _ (ITruncFS BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType _ (ITruncSatFU BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType _ (ITruncSatFU BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType _ (ITruncSatFU BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType _ (ITruncSatFU BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType _ (ITruncSatFS BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType _ (ITruncSatFS BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType _ (ITruncSatFS BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType _ (ITruncSatFS BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType _ I64ExtendSI32 = return $ I32 ==> I64
|
||||
getInstrType _ I64ExtendUI32 = return $ I32 ==> I64
|
||||
getInstrType _ (FConvertIU BS32 BS32) = return $ I32 ==> F32
|
||||
getInstrType _ (FConvertIU BS32 BS64) = return $ I64 ==> F32
|
||||
getInstrType _ (FConvertIU BS64 BS32) = return $ I32 ==> F64
|
||||
getInstrType _ (FConvertIU BS64 BS64) = return $ I64 ==> F64
|
||||
getInstrType _ (FConvertIU (BS128 _) (BS128 _)) = return $ V128 ==> V128
|
||||
getInstrType _ (FConvertIS BS32 BS32) = return $ I32 ==> F32
|
||||
getInstrType _ (FConvertIS BS32 BS64) = return $ I64 ==> F32
|
||||
getInstrType _ (FConvertIS BS64 BS32) = return $ I32 ==> F64
|
||||
getInstrType _ (FConvertIS BS64 BS64) = return $ I64 ==> F64
|
||||
getInstrType _ (FConvertIS (BS128 _) (BS128 _)) = return $ V128 ==> V128
|
||||
getInstrType _ F32DemoteF64 = return $ F64 ==> F32
|
||||
getInstrType _ F64PromoteF32 = return $ F32 ==> F64
|
||||
getInstrType _ (IReinterpretF BS32) = return $ F32 ==> I32
|
||||
getInstrType _ (IReinterpretF BS64) = return $ F64 ==> I64
|
||||
getInstrType _ (FReinterpretI BS32) = return $ I32 ==> F32
|
||||
getInstrType _ (FReinterpretI BS64) = return $ I64 ==> F64
|
||||
getInstrType _ I8x16Swizzle =
|
||||
return $ [V128, V128] ==> V128
|
||||
getInstrType _ (I8x16Shuffle idxs) = do
|
||||
when (any (>= 32) idxs) $ throwError LaneIndexOutOfRange
|
||||
return $ [V128, V128] ==> V128
|
||||
getInstrType _ (V128Splat shape) =
|
||||
return $ getShapeElemType shape ==> V128
|
||||
getInstrType _ (V128ExtractLane shape idx _) = do
|
||||
when (idx >= lanesCount shape) $ throwError LaneIndexOutOfRange
|
||||
return $ V128 ==> getShapeElemType shape
|
||||
getInstrType _ (V128ReplaceLane shape idx) = do
|
||||
when (idx >= lanesCount shape) $ throwError LaneIndexOutOfRange
|
||||
return $ [V128, getShapeElemType shape] ==> V128
|
||||
getInstrType _ (V128AllTrue _) =
|
||||
return $ V128 ==> I32
|
||||
getInstrType _ V128AnyTrue =
|
||||
return $ V128 ==> I32
|
||||
getInstrType _ V128BitSelect =
|
||||
return $ [V128, V128, V128] ==> V128
|
||||
getInstrType _ (V128BitMask _) =
|
||||
return $ V128 ==> I32
|
||||
getInstrType _ (V128Narrow _ _ _) =
|
||||
return $ [V128, V128] ==> V128
|
||||
getInstrType _ F64x2PromoteLowF32x4 =
|
||||
return $ V128 ==> V128
|
||||
getInstrType _ F32x4DemoteF64x2Zero =
|
||||
return $ V128 ==> V128
|
||||
getInstrType _ (V128IExtend _ _ _ _) =
|
||||
return $ V128 ==> V128
|
||||
getInstrType _ (I32x4TruncSatF _ _) =
|
||||
return $ V128 ==> V128
|
||||
getInstrType _ I32x4DotI16x8S =
|
||||
return $[V128, V128] ==> V128
|
||||
getInstrType _ I16x8Q15MulrSatS =
|
||||
return $ [V128, V128] ==> V128
|
||||
if length mems < 1 then throwError (MemoryIndexOutOfRange 0) else return $ empty ==> I32
|
||||
getInstrType GrowMemory = do
|
||||
Ctx { mems } <- ask
|
||||
if length mems < 1 then throwError (MemoryIndexOutOfRange 0) else return $ I32 ==> I32
|
||||
getInstrType (I32Const _) = return $ empty ==> I32
|
||||
getInstrType (I64Const _) = return $ empty ==> I64
|
||||
getInstrType (F32Const _) = return $ empty ==> F32
|
||||
getInstrType (F64Const _) = return $ empty ==> F64
|
||||
getInstrType (IUnOp BS32 _) = return $ I32 ==> I32
|
||||
getInstrType (IUnOp BS64 _) = return $ I64 ==> I64
|
||||
getInstrType (IBinOp BS32 _) = return $ [I32, I32] ==> I32
|
||||
getInstrType (IBinOp BS64 _) = return $ [I64, I64] ==> I64
|
||||
getInstrType I32Eqz = return $ I32 ==> I32
|
||||
getInstrType I64Eqz = return $ I64 ==> I32
|
||||
getInstrType (IRelOp BS32 _) = return $ [I32, I32] ==> I32
|
||||
getInstrType (IRelOp BS64 _) = return $ [I64, I64] ==> I32
|
||||
getInstrType (FUnOp BS32 _) = return $ F32 ==> F32
|
||||
getInstrType (FUnOp BS64 _) = return $ F64 ==> F64
|
||||
getInstrType (FBinOp BS32 _) = return $ [F32, F32] ==> F32
|
||||
getInstrType (FBinOp BS64 _) = return $ [F64, F64] ==> F64
|
||||
getInstrType (FRelOp BS32 _) = return $ [F32, F32] ==> I32
|
||||
getInstrType (FRelOp BS64 _) = return $ [F64, F64] ==> I32
|
||||
getInstrType I32WrapI64 = return $ I64 ==> I32
|
||||
getInstrType (ITruncFU BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType (ITruncFU BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType (ITruncFU BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType (ITruncFU BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType (ITruncFS BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType (ITruncFS BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType (ITruncFS BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType (ITruncFS BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType (ITruncSatFU BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType (ITruncSatFU BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType (ITruncSatFU BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType (ITruncSatFU BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType (ITruncSatFS BS32 BS32) = return $ F32 ==> I32
|
||||
getInstrType (ITruncSatFS BS32 BS64) = return $ F64 ==> I32
|
||||
getInstrType (ITruncSatFS BS64 BS32) = return $ F32 ==> I64
|
||||
getInstrType (ITruncSatFS BS64 BS64) = return $ F64 ==> I64
|
||||
getInstrType I64ExtendSI32 = return $ I32 ==> I64
|
||||
getInstrType I64ExtendUI32 = return $ I32 ==> I64
|
||||
getInstrType (FConvertIU BS32 BS32) = return $ I32 ==> F32
|
||||
getInstrType (FConvertIU BS32 BS64) = return $ I64 ==> F32
|
||||
getInstrType (FConvertIU BS64 BS32) = return $ I32 ==> F64
|
||||
getInstrType (FConvertIU BS64 BS64) = return $ I64 ==> F64
|
||||
getInstrType (FConvertIS BS32 BS32) = return $ I32 ==> F32
|
||||
getInstrType (FConvertIS BS32 BS64) = return $ I64 ==> F32
|
||||
getInstrType (FConvertIS BS64 BS32) = return $ I32 ==> F64
|
||||
getInstrType (FConvertIS BS64 BS64) = return $ I64 ==> F64
|
||||
getInstrType F32DemoteF64 = return $ F64 ==> F32
|
||||
getInstrType F64PromoteF32 = return $ F32 ==> F64
|
||||
getInstrType (IReinterpretF BS32) = return $ F32 ==> I32
|
||||
getInstrType (IReinterpretF BS64) = return $ F64 ==> I64
|
||||
getInstrType (FReinterpretI BS32) = return $ I32 ==> F32
|
||||
getInstrType (FReinterpretI BS64) = return $ I64 ==> F64
|
||||
|
||||
getShapeElemType :: SimdShape -> ValueType
|
||||
getShapeElemType I8x16 = I32
|
||||
getShapeElemType I16x8 = I32
|
||||
getShapeElemType I32x4 = I32
|
||||
getShapeElemType I64x2 = I64
|
||||
getShapeElemType F32x4 = F32
|
||||
getShapeElemType F64x2 = F64
|
||||
|
||||
lanesCount :: SimdShape -> Natural
|
||||
lanesCount shape = case shape of
|
||||
I8x16 -> 16
|
||||
I16x8 -> 8
|
||||
I32x4 -> 4
|
||||
I64x2 -> 2
|
||||
F32x4 -> 4
|
||||
F64x2 -> 2
|
||||
I128x1 -> 1
|
||||
|
||||
replace :: (Eq a) => a -> a -> [a] -> [a]
|
||||
replace _ _ [] = []
|
||||
@@ -668,46 +416,25 @@ getExpressionTypeWithInput inp = fmap (inp `Arrow`) . foldM go inp
|
||||
where
|
||||
go :: [VType] -> Instruction Natural -> Checker [VType]
|
||||
go stack instr = do
|
||||
(f `Arrow` t) <- getInstrType stack instr
|
||||
(f `Arrow` t) <- getInstrType instr
|
||||
matchStack stack (reverse f) t
|
||||
|
||||
isRef :: ValueType -> Bool
|
||||
isRef (Func) = True
|
||||
isRef (Extern) = True
|
||||
isRef _ = False
|
||||
|
||||
matchStack :: [VType] -> [VType] -> [VType] -> Checker [VType]
|
||||
matchStack stack@(Any:_) _arg res = return $ res ++ stack
|
||||
matchStack (Val v:stack) (Val v':args) res =
|
||||
if v == v'
|
||||
then matchStack stack args res
|
||||
else throwError $ TypeMismatch ((reverse $ Val v':args) `Arrow` res) ([] `Arrow` (Val v:stack))
|
||||
matchStack _ (Any:_) res = return $ res
|
||||
matchStack (Val v:stack) (Var:args) res =
|
||||
let subst = replace Var (Val v) in
|
||||
matchStack stack (subst args) (subst res)
|
||||
matchStack (Var:stack) (Val v:args) res =
|
||||
let subst = replace Var (Val v) in
|
||||
matchStack stack (subst args) (subst res)
|
||||
matchStack (Val v:stack) (NonRefVar:args) res =
|
||||
let subst = replace NonRefVar (Val v) in
|
||||
if isRef v
|
||||
then throwError $ TypeMismatch (empty ==> empty) (empty ==> empty)
|
||||
else matchStack stack (subst args) (subst res)
|
||||
matchStack (NonRefVar:stack) (Val v:args) res =
|
||||
let subst = replace NonRefVar (Val v) in
|
||||
if isRef v
|
||||
then throwError $ TypeMismatch (empty ==> empty) (empty ==> empty)
|
||||
else matchStack stack (subst args) (subst res)
|
||||
matchStack (Var:stack) (NonRefVar:args) res =
|
||||
let subst = replace NonRefVar NonRefVar in
|
||||
matchStack stack (subst args) (subst res)
|
||||
matchStack (NonRefVar:stack) (Var:args) res =
|
||||
let subst = replace Var NonRefVar in
|
||||
matchStack stack (subst args) (subst res)
|
||||
matchStack stack [] res = return $ res ++ stack
|
||||
matchStack [] args res = throwError $ TypeMismatch ((reverse args) `Arrow` res) ([] `Arrow` [])
|
||||
matchStack st args res = error $ "inconsistent checker state: " ++ show (st, args, res)
|
||||
|
||||
matchStack :: [VType] -> [VType] -> [VType] -> Checker [VType]
|
||||
matchStack stack@(Any:_) _arg res = return $ res ++ stack
|
||||
matchStack (Val v:stack) (Val v':args) res =
|
||||
if v == v'
|
||||
then matchStack stack args res
|
||||
else throwError $ TypeMismatch ((reverse $ Val v':args) `Arrow` res) ([] `Arrow` (Val v:stack))
|
||||
matchStack _ (Any:_) res = return $ res
|
||||
matchStack (Val v:stack) (Var:args) res =
|
||||
let subst = replace Var (Val v) in
|
||||
matchStack stack (subst args) (subst res)
|
||||
matchStack (Var:stack) (Val v:args) res =
|
||||
let subst = replace Var (Val v) in
|
||||
matchStack stack (subst args) (subst res)
|
||||
matchStack stack [] res = return $ res ++ stack
|
||||
matchStack [] args res = throwError $ TypeMismatch ((reverse args) `Arrow` res) ([] `Arrow` [])
|
||||
matchStack _ _ _ = error "inconsistent checker state"
|
||||
|
||||
getExpressionType :: Expression -> Checker Arrow
|
||||
getExpressionType = getExpressionTypeWithInput []
|
||||
@@ -718,9 +445,6 @@ isConstExpression ((I32Const _):rest) = isConstExpression rest
|
||||
isConstExpression ((I64Const _):rest) = isConstExpression rest
|
||||
isConstExpression ((F32Const _):rest) = isConstExpression rest
|
||||
isConstExpression ((F64Const _):rest) = isConstExpression rest
|
||||
isConstExpression ((V128Const _):rest) = isConstExpression rest
|
||||
isConstExpression ((RefNull _):rest) = isConstExpression rest
|
||||
isConstExpression ((RefFunc _):rest) = isConstExpression rest
|
||||
isConstExpression ((GetGlobal idx):rest) = do
|
||||
Ctx {globals, importedGlobals} <- ask
|
||||
if importedGlobals <= idx
|
||||
@@ -740,26 +464,20 @@ getFuncTypes Module {types, functions, imports} =
|
||||
getFuncType _ = Nothing
|
||||
|
||||
ctxFromModule :: [ValueType] -> [[ValueType]] -> [ValueType] -> Module -> Ctx
|
||||
ctxFromModule locals labels returns m =
|
||||
let Module {types, tables, mems, globals, imports, elems, exports, datas} = m in
|
||||
ctxFromModule locals labels returns m@Module {types, tables, mems, globals, imports} =
|
||||
let tableImports = catMaybes $ map getTableType imports in
|
||||
let memsImports = catMaybes $ map getMemType imports in
|
||||
let globalImports = catMaybes $ map getGlobalType imports in
|
||||
Ctx {
|
||||
types,
|
||||
funcs = getFuncTypes m,
|
||||
tableTypes = tableImports ++ map (\(Table t) -> t) tables,
|
||||
elems = map elemType elems,
|
||||
datas = map dataMode datas,
|
||||
tables = tableImports ++ map (\(Table t) -> t) tables,
|
||||
mems = memsImports ++ map (\(Memory l) -> l) mems,
|
||||
globals = globalImports ++ map (\(Global g _) -> g) globals,
|
||||
locals,
|
||||
labels,
|
||||
returns,
|
||||
importedGlobals = fromIntegral $ length globalImports,
|
||||
refs = Set.unions $ map getElemRefs elems
|
||||
++ map getGlobalRefs globals
|
||||
++ map getExportRefs exports
|
||||
importedGlobals = fromIntegral $ length globalImports
|
||||
}
|
||||
where
|
||||
getTableType (Import _ _ (ImportTable tableType)) = Just tableType
|
||||
@@ -771,19 +489,6 @@ ctxFromModule locals labels returns m =
|
||||
getGlobalType (Import _ _ (ImportGlobal gl)) = Just gl
|
||||
getGlobalType _ = Nothing
|
||||
|
||||
getElemRefs ElemSegment{ elemType = FuncRef, elements} =
|
||||
foldl extractRef Set.empty elements
|
||||
where
|
||||
extractRef refs [RefFunc idx] = Set.insert idx refs
|
||||
extractRef refs _ = refs
|
||||
getElemRefs _ = Set.empty
|
||||
|
||||
getGlobalRefs Global {initializer = [RefFunc idx]} = Set.singleton idx
|
||||
getGlobalRefs _ = Set.empty
|
||||
|
||||
getExportRefs Export {desc = ExportFunc idx} = Set.singleton idx
|
||||
getExportRefs _ = Set.empty
|
||||
|
||||
isFunctionValid :: Function -> Validator
|
||||
isFunctionValid Function {funcType, localTypes = locals, body} mod@Module {types} =
|
||||
if fromIntegral funcType < length types
|
||||
@@ -804,7 +509,10 @@ tablesShouldBeValid :: Validator
|
||||
tablesShouldBeValid Module { imports, tables } =
|
||||
let tableImports = filter isTableImport imports in
|
||||
let res = foldMap (\Import { desc = ImportTable t } -> isValidTableType t) tableImports in
|
||||
foldl' (\r (Table t) -> r <> isValidTableType t) res tables
|
||||
let res' = foldl' (\r (Table t) -> r <> isValidTableType t) res tables in
|
||||
if length tableImports + length tables <= 1
|
||||
then res'
|
||||
else Left MoreThanOneTable
|
||||
where
|
||||
isValidTableType :: TableType -> ValidationResult
|
||||
isValidTableType (TableType (Limit min max) _) =
|
||||
@@ -849,30 +557,24 @@ elemsShouldBeValid m@Module { elems, functions, tables, imports } =
|
||||
foldMap (isElemValid ctx) elems
|
||||
where
|
||||
isElemValid :: Ctx -> ElemSegment -> ValidationResult
|
||||
isElemValid ctx (ElemSegment elemType mode elements) = do
|
||||
forM_ elements $ \elem -> runChecker ctx $ do
|
||||
arr <- getExpressionType elem
|
||||
isConstExpression elem
|
||||
unless (isValidRef elemType arr)
|
||||
$ throwError $ RefTypeMismatch elemType elemType
|
||||
case mode of
|
||||
Active tableIdx offset -> runChecker ctx $ do
|
||||
isElemValid ctx (ElemSegment tableIdx offset funs) =
|
||||
let check = runChecker ctx $ do
|
||||
isConstExpression offset
|
||||
t <- getExpressionType offset
|
||||
unless (isArrowMatch (empty ==> I32) t) $ do
|
||||
throwError $ TypeMismatch t (empty ==> I32)
|
||||
let tableImports = filter isTableImport imports
|
||||
when (tableIdx >= fromIntegral (length tableImports + length tables)) $ do
|
||||
throwError $ TableIndexOutOfRange tableIdx
|
||||
let TableType _ tableType = tableTypes ctx !! (fromIntegral tableIdx)
|
||||
when (tableType /= elemType) $ do
|
||||
throwError $ RefTypeMismatch elemType tableType
|
||||
_ -> return ()
|
||||
|
||||
isValidRef :: ElemType -> Arrow -> Bool
|
||||
isValidRef FuncRef arr | arr == (empty ==> Func) = True
|
||||
isValidRef ExternRef arr | arr == (empty ==> Extern) = True
|
||||
isValidRef _ _ = False
|
||||
if isArrowMatch (empty ==> I32) t
|
||||
then return ()
|
||||
else throwError $ TypeMismatch t (empty ==> I32)
|
||||
in
|
||||
let tableImports = filter isTableImport imports in
|
||||
let isTableIndexValid =
|
||||
if tableIdx < (fromIntegral $ length tableImports + length tables)
|
||||
then return ()
|
||||
else Left (TableIndexOutOfRange tableIdx)
|
||||
in
|
||||
let funImports = filter isFuncImport imports in
|
||||
let funsLength = fromIntegral $ length functions + length funImports in
|
||||
let isFunsValid = foldMap (\i -> if i < funsLength then return () else Left FunctionIndexOutOfRange) funs in
|
||||
check <> isFunsValid <> isTableIndexValid
|
||||
|
||||
datasShouldBeValid :: Validator
|
||||
datasShouldBeValid m@Module { datas, mems, imports } =
|
||||
@@ -880,7 +582,7 @@ datasShouldBeValid m@Module { datas, mems, imports } =
|
||||
foldMap (isDataValid ctx) datas
|
||||
where
|
||||
isDataValid :: Ctx -> DataSegment -> ValidationResult
|
||||
isDataValid ctx (DataSegment (ActiveData memIdx offset) _) =
|
||||
isDataValid ctx (DataSegment memIdx offset _) =
|
||||
let check = runChecker ctx $ do
|
||||
isConstExpression offset
|
||||
t <- getExpressionType offset
|
||||
@@ -892,7 +594,6 @@ datasShouldBeValid m@Module { datas, mems, imports } =
|
||||
if memIdx < (fromIntegral $ length memImports + length mems)
|
||||
then check
|
||||
else Left (MemoryIndexOutOfRange memIdx)
|
||||
isDataValid ctx (DataSegment PassiveData _) = return ()
|
||||
|
||||
startShouldBeValid :: Validator
|
||||
startShouldBeValid Module { start = Nothing } = return ()
|
||||
@@ -901,7 +602,7 @@ startShouldBeValid m@Module { start = Just (StartFunction idx) } =
|
||||
let i = fromIntegral idx in
|
||||
if length types > i
|
||||
then if FuncType [] [] == types !! i then return () else Left InvalidStartFunctionType
|
||||
else Left $ FunctionIndexOutOfRange $ fromIntegral i
|
||||
else Left FunctionIndexOutOfRange
|
||||
|
||||
exportsShouldBeValid :: Validator
|
||||
exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals } =
|
||||
@@ -914,7 +615,7 @@ exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals
|
||||
|
||||
isExportValid :: Export -> ValidationResult
|
||||
isExportValid (Export _ (ExportFunc funIdx)) =
|
||||
if fromIntegral funIdx < length funcImports + length functions then return () else Left (FunctionIndexOutOfRange funIdx)
|
||||
if fromIntegral funIdx < length funcImports + length functions then return () else Left FunctionIndexOutOfRange
|
||||
isExportValid (Export _ (ExportTable tableIdx)) =
|
||||
if fromIntegral tableIdx < length tableImports + length tables then return () else Left (TableIndexOutOfRange tableIdx)
|
||||
isExportValid (Export _ (ExportMemory memIdx)) =
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
resolver: lts-20.23
|
||||
resolver: lts-16.5
|
||||
packages:
|
||||
- '.'
|
||||
extra-deps: []
|
||||
flags: {}
|
||||
extra-package-dbs: []
|
||||
extra-package-dbs: []
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@
|
||||
packages: []
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: 4c972e067bae16b95961dbfdd12e07f1ee6c8fffabbfa05c3d65100b03f548b7
|
||||
size: 650253
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/23.yaml
|
||||
original: lts-20.23
|
||||
size: 531707
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/5.yaml
|
||||
sha256: 9751e25e0af5713a53ddcfcc79564b082c71b1b357fadef0d85672a5b5ba3703
|
||||
original: lts-16.5
|
||||
|
||||
+17
-13
@@ -16,16 +16,20 @@ import qualified Data.List as List
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
files <-
|
||||
filter (List.isSuffixOf ".wast")
|
||||
<$> Directory.listDirectory "tests/spec"
|
||||
-- let files = ["align.wast"]
|
||||
scriptTestCases <- (`mapM` files) $ \file -> do
|
||||
test <- LBS.readFile ("tests/spec/" ++ file)
|
||||
return $ testCase file $ do
|
||||
case Wasm.parseScript test of
|
||||
Right script ->
|
||||
Script.runScript (\msg assert -> assertFailure ("Failed assert: " ++ msg ++ ". Assert " ++ show assert)) script
|
||||
Left error ->
|
||||
assertFailure $ "Failed to parse with error: " ++ show error
|
||||
defaultMain $ testGroup "Wasm Core Test Suit" scriptTestCases
|
||||
let groups = [
|
||||
("Core Tests", "tests/spec"),
|
||||
("Reference Types Proposal", "tests/spec/proposals/reference-types")
|
||||
]
|
||||
testGroups <- (`mapM` groups) $ \(groupName, dir) -> do
|
||||
files <- filter (List.isSuffixOf ".wast") <$> Directory.listDirectory dir
|
||||
-- let files = ["const.wast"]
|
||||
scriptTestCases <- (`mapM` files) $ \file -> do
|
||||
test <- LBS.readFile (dir ++ "/" ++ file)
|
||||
return $ testCase file $ do
|
||||
case Wasm.parseScript test of
|
||||
Right script ->
|
||||
Script.runScript (\msg assert -> assertFailure ("Failed assert: " ++ msg ++ ". Assert " ++ show assert)) script
|
||||
Left error ->
|
||||
assertFailure $ "Failed to parse with error: " ++ show error
|
||||
return $ testGroup groupName scriptTestCases
|
||||
defaultMain $ testGroup "Wasm Test Suit" testGroups
|
||||
|
||||
+1
-1
Submodule tests/spec updated: 68c6f83f33...01efde8102
+15
-15
@@ -1,6 +1,6 @@
|
||||
cabal-version: 2.2
|
||||
name: wasm
|
||||
version: 1.1.2
|
||||
version: 1.0.1.0
|
||||
synopsis: WebAssembly Language Toolkit and Interpreter
|
||||
description:
|
||||
Library for parsing and interpreting WebAssembly, including:
|
||||
@@ -16,9 +16,9 @@ license: MIT
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
category: Language
|
||||
homepage: https://github.com/SPY/haskell-wasm
|
||||
bug-reports: https://github.com/SPY/haskell-wasm/issues
|
||||
tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.4, GHC==9.2.7
|
||||
homepage: https:github.com/SPY/haskell-wasm
|
||||
bug-reports: https:github.com/SPY/haskell-wasm/issues
|
||||
tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.4
|
||||
extra-source-files:
|
||||
README.md
|
||||
src/Language/Wasm/Parser.y
|
||||
@@ -27,20 +27,20 @@ extra-source-files:
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/SPY/haskell-wasm
|
||||
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
Language.Wasm.Script
|
||||
Language.Wasm.Lexer
|
||||
Language.Wasm.Structure
|
||||
Language.Wasm
|
||||
other-modules:
|
||||
Language.Wasm.Binary
|
||||
Language.Wasm.Builder
|
||||
Language.Wasm.FloatUtils
|
||||
Language.Wasm.Interpreter
|
||||
Language.Wasm.Parser
|
||||
Language.Wasm.Validate
|
||||
Language.Wasm.Binary
|
||||
Language.Wasm.Builder
|
||||
Language.Wasm
|
||||
other-modules:
|
||||
Language.Wasm.FloatUtils
|
||||
Paths_wasm
|
||||
autogen-modules:
|
||||
Paths_wasm
|
||||
@@ -56,18 +56,18 @@ library
|
||||
, happy:happy >=1.9.4 && < 1.21
|
||||
build-depends:
|
||||
array >=0.5 && < 0.6
|
||||
, base >=4.11 && < 5
|
||||
, base >=4.6 && < 5
|
||||
, bytestring >=0.10 && < 0.12
|
||||
, cereal >=0.5 && < 0.6
|
||||
, containers >=0.5 && < 0.7
|
||||
, deepseq >=1.4 && < 1.5
|
||||
, ieee754 >=0.8 && < 0.9
|
||||
, mtl >=2.2.1 && < 2.4
|
||||
, mtl >=2.2.1 && < 2.3
|
||||
, primitive >=0.7 && < 0.8
|
||||
, text >=1.1 && < 3
|
||||
, transformers >=0.4 && < 0.7
|
||||
, text >=1.1 && < 1.3
|
||||
, transformers >=0.4 && < 0.6
|
||||
, utf8-string >=1.0 && < 1.1
|
||||
, vector >=0.12.2 && < 0.14
|
||||
, vector >=0.12 && < 0.13
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite test
|
||||
|
||||
Reference in New Issue
Block a user