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