Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fab53969a | |||
| a3de890895 | |||
| 9eadc50ee3 | |||
| 5b6b043049 | |||
| bb2a6cea7f | |||
| f23dc20d57 | |||
| 2b822a8d44 | |||
| 46f95dd11d | |||
| 2d4ad42549 | |||
| c044f3f556 | |||
| d0403ad554 | |||
| d321bf6a9c | |||
| ec83b12ccd | |||
| 9f17885106 | |||
| ea50c54900 | |||
| 4e9cb99e69 | |||
| b2f001aea8 | |||
| f445340568 | |||
| 2d15ddfa36 | |||
| 0b44ee13b8 | |||
| e9140ae70c | |||
| cbeb4bb61d | |||
| 8c97e2c328 | |||
| 6e9c4d969a | |||
| 179b7ce180 | |||
| fd72ee9d23 | |||
| b6c0ef4462 | |||
| 51cf7e753b | |||
| fe6c31984b | |||
| f2f5fcc56f | |||
| a5ffe38a1c | |||
| 724973b15e | |||
| a58bb32d98 | |||
| 0e0b1abbba | |||
| 418847226e | |||
| efba9c2b50 | |||
| f6d0783fd0 | |||
| 57c4184c18 | |||
| 3be70781b8 | |||
| 5256d5063f | |||
| 2bf6e88072 | |||
| 32392dcc29 | |||
| c743e11ffd | |||
| 32357d68fe | |||
| 83fea36c02 | |||
| 43cbfe653f | |||
| 71d332e3dc | |||
| 4e9105717b | |||
| df15d3c4d1 | |||
| b1da37ac03 | |||
| 82defff076 | |||
| 6eb3acde17 | |||
| e388e21370 | |||
| 95fdcc2f80 | |||
| 5bcd863671 | |||
| 4753ebceb4 | |||
| de40134caf | |||
| 286ee40489 | |||
| c08e81fa40 | |||
| 8af7b45681 | |||
| 960acac955 | |||
| 99532adb63 | |||
| c8f1bc9186 | |||
| 66458e11f3 |
@@ -8,3 +8,5 @@ dist-newstyle/
|
|||||||
doc/
|
doc/
|
||||||
setup-config
|
setup-config
|
||||||
wasm-*-docs.tar.gz
|
wasm-*-docs.tar.gz
|
||||||
|
cache
|
||||||
|
packagedb
|
||||||
@@ -20,9 +20,10 @@
|
|||||||
* [ ] 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
|
||||||
Clond sources to directory and use `stack` for running tests:
|
Clone sources to directory and use `stack` for running tests:
|
||||||
```
|
```
|
||||||
stack build && stack test
|
stack build && stack test
|
||||||
```
|
```
|
||||||
|
|||||||
+151
-20
@@ -244,7 +244,13 @@ instance Serialize FuncType where
|
|||||||
|
|
||||||
instance Serialize ElemType where
|
instance Serialize ElemType where
|
||||||
put FuncRef = putWord8 0x70
|
put FuncRef = putWord8 0x70
|
||||||
get = byteGuard 0x70 >> return FuncRef
|
put ExternRef = putWord8 0x6F
|
||||||
|
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
|
||||||
@@ -320,6 +326,12 @@ 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
|
||||||
@@ -353,16 +365,50 @@ 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 typeIdx) = putWord8 0x11 >> putULEB128 typeIdx >> putWord8 0x00
|
put (CallIndirect tableIdx typeIdx) = putWord8 0x11 >> putULEB128 typeIdx >> putULEB128 tableIdx
|
||||||
|
-- Reference instructions
|
||||||
|
put (RefNull refType) = putWord8 0xD0 >> put refType
|
||||||
|
put RefIsNull = putWord8 0xD1
|
||||||
|
put (RefFunc index) = putWord8 0xD2 >> putULEB128 index
|
||||||
-- Parametric instructions
|
-- Parametric instructions
|
||||||
put Drop = putWord8 0x1A
|
put Drop = putWord8 0x1A
|
||||||
put Select = putWord8 0x1B
|
put (Select Nothing) = 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
|
||||||
@@ -387,8 +433,26 @@ 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 CurrentMemory = putWord8 0x3F >> putWord8 0x00
|
put MemorySize = putWord8 0x3F >> putWord8 0x00
|
||||||
put GrowMemory = putWord8 0x40 >> putWord8 0x00
|
put MemoryGrow = putWord8 0x40 >> putWord8 0x00
|
||||||
|
put (MemoryInit dataIdx) = do
|
||||||
|
putWord8 0xFC
|
||||||
|
putULEB128 (0x08 :: Word32)
|
||||||
|
putULEB128 dataIdx
|
||||||
|
putWord8 0
|
||||||
|
put (DataDrop dataIdx) = do
|
||||||
|
putWord8 0xFC
|
||||||
|
putULEB128 (0x09 :: Word32)
|
||||||
|
putULEB128 dataIdx
|
||||||
|
put MemoryCopy = do
|
||||||
|
putWord8 0xFC
|
||||||
|
putULEB128 (0x0A :: Word32)
|
||||||
|
putWord8 0
|
||||||
|
putWord8 0
|
||||||
|
put MemoryFill = do
|
||||||
|
putWord8 0xFC
|
||||||
|
putULEB128 (0x0B :: Word32)
|
||||||
|
putWord8 0
|
||||||
-- 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)
|
||||||
@@ -553,11 +617,15 @@ instance Serialize (Instruction Natural) where
|
|||||||
0x10 -> Call <$> getULEB128 32
|
0x10 -> Call <$> getULEB128 32
|
||||||
0x11 -> do
|
0x11 -> do
|
||||||
typeIdx <- getULEB128 32
|
typeIdx <- getULEB128 32
|
||||||
byteGuard 0x00
|
tableIdx <- getULEB128 32
|
||||||
return $ CallIndirect typeIdx
|
return $ CallIndirect tableIdx 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
|
0x1B -> return $ Select Nothing
|
||||||
-- Variable instructions
|
-- Variable instructions
|
||||||
0x20 -> GetLocal <$> getULEB128 32
|
0x20 -> GetLocal <$> getULEB128 32
|
||||||
0x21 -> SetLocal <$> getULEB128 32
|
0x21 -> SetLocal <$> getULEB128 32
|
||||||
@@ -588,8 +656,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 $ CurrentMemory)
|
0x3F -> byteGuard 0x00 >> (return $ MemorySize)
|
||||||
0x40 -> byteGuard 0x00 >> (return $ GrowMemory)
|
0x40 -> byteGuard 0x00 >> (return $ MemoryGrow)
|
||||||
-- Numeric instructions
|
-- Numeric instructions
|
||||||
0x41 -> I32Const <$> getSLEB128 32
|
0x41 -> I32Const <$> getSLEB128 32
|
||||||
0x42 -> I64Const <$> getSLEB128 64
|
0x42 -> I64Const <$> getSLEB128 64
|
||||||
@@ -735,7 +803,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"
|
||||||
_ -> fail "Unknown byte value in place of instruction opcode"
|
byte -> fail $ "Unknown byte value in place of instruction opcode: " ++ (show byte)
|
||||||
|
|
||||||
putExpression :: Expression -> Put
|
putExpression :: Expression -> Put
|
||||||
putExpression expr = do
|
putExpression expr = do
|
||||||
@@ -793,11 +861,56 @@ instance Serialize Export where
|
|||||||
get = Export <$> getName <*> get
|
get = Export <$> getName <*> get
|
||||||
|
|
||||||
instance Serialize ElemSegment where
|
instance Serialize ElemSegment where
|
||||||
put (ElemSegment tableIndex offset funcIndexes) = do
|
put (ElemSegment elemType Passive elements) = 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
|
||||||
putVec $ map Index funcIndexes
|
put elemType
|
||||||
get = ElemSegment <$> getULEB128 32 <*> getExpression <*> (map unIndex <$> getVec)
|
putVec $ map Expr elements
|
||||||
|
put (ElemSegment elemType Declarative elements) = do
|
||||||
|
putWord8 0x07
|
||||||
|
put elemType
|
||||||
|
putVec $ map Expr elements
|
||||||
|
|
||||||
|
get = do
|
||||||
|
op <- getWord8
|
||||||
|
let funcIndexes = map ((:[]) . RefFunc . unIndex) <$> getVec
|
||||||
|
let elemKind = byteGuard 0x00 >> return FuncRef
|
||||||
|
case op 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)
|
||||||
|
|
||||||
@@ -824,17 +937,35 @@ instance Serialize Function where
|
|||||||
return $ Function 0 locals body
|
return $ Function 0 locals body
|
||||||
|
|
||||||
instance Serialize DataSegment where
|
instance Serialize DataSegment where
|
||||||
put (DataSegment memIdx offset init) = do
|
put (DataSegment (ActiveData 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
|
||||||
memIdx <- getULEB128 32
|
op <- getULEB128 32
|
||||||
offset <- getExpression
|
case (op :: Word8) of
|
||||||
len <- getULEB128 32
|
0x00 -> do
|
||||||
init <- getLazyByteString len
|
offset <- getExpression
|
||||||
return $ DataSegment memIdx offset init
|
len <- getULEB128 32
|
||||||
|
init <- getLazyByteString len
|
||||||
|
return $ DataSegment (ActiveData 0 offset) init
|
||||||
|
0x01 -> do
|
||||||
|
len <- getULEB128 32
|
||||||
|
init <- getLazyByteString len
|
||||||
|
return $ DataSegment PassiveData init
|
||||||
|
0x02 -> do
|
||||||
|
memIdx <- getULEB128 32
|
||||||
|
offset <- getExpression
|
||||||
|
len <- getULEB128 32
|
||||||
|
init <- getLazyByteString len
|
||||||
|
return $ DataSegment (ActiveData memIdx offset) init
|
||||||
|
byte -> fail $ "unknown data segment type: " ++ show byte
|
||||||
|
|
||||||
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]
|
appendExpr [Select Nothing]
|
||||||
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 [CurrentMemory] >> return Proxy
|
memorySize = appendExpr [MemorySize] >> 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 [GrowMemory]
|
growMemory size = produce size >> appendExpr [MemoryGrow]
|
||||||
|
|
||||||
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 idx]
|
appendExpr [CallIndirect 0 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 0 (genExpr 0 (produce offset)) bytes] }
|
target = m { datas = datas m ++ [DataSegment (ActiveData 0 (genExpr 0 (produce offset))) bytes] }
|
||||||
}
|
}
|
||||||
|
|
||||||
asWord32 :: Int32 -> Word32
|
asWord32 :: Int32 -> Word32
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ module Language.Wasm.Interpreter (
|
|||||||
ExternalValue(..),
|
ExternalValue(..),
|
||||||
ExportInstance(..),
|
ExportInstance(..),
|
||||||
GlobalInstance(..),
|
GlobalInstance(..),
|
||||||
|
MemoryInstance(..),
|
||||||
|
MemoryStore,
|
||||||
Imports,
|
Imports,
|
||||||
HostItem(..),
|
HostItem(..),
|
||||||
|
Address,
|
||||||
instantiate,
|
instantiate,
|
||||||
invoke,
|
invoke,
|
||||||
invokeExport,
|
invokeExport,
|
||||||
@@ -20,7 +23,8 @@ module Language.Wasm.Interpreter (
|
|||||||
emptyImports,
|
emptyImports,
|
||||||
makeHostModule,
|
makeHostModule,
|
||||||
makeMutGlobal,
|
makeMutGlobal,
|
||||||
makeConstGlobal
|
makeConstGlobal,
|
||||||
|
getMemory
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
@@ -30,6 +34,8 @@ import Data.Maybe (fromMaybe, isNothing)
|
|||||||
|
|
||||||
import Data.Vector (Vector, (!), (!?), (//))
|
import Data.Vector (Vector, (!), (!?), (//))
|
||||||
import qualified Data.Vector as Vector
|
import qualified Data.Vector as Vector
|
||||||
|
import Data.Vector.Mutable (IOVector)
|
||||||
|
import qualified Data.Vector.Mutable as MVector
|
||||||
import qualified Data.Primitive.ByteArray as ByteArray
|
import qualified Data.Primitive.ByteArray as ByteArray
|
||||||
import qualified Data.Primitive.Types as Primitive
|
import qualified Data.Primitive.Types as Primitive
|
||||||
import qualified Control.Monad.Primitive as Primitive
|
import qualified Control.Monad.Primitive as Primitive
|
||||||
@@ -65,11 +71,15 @@ import Language.Wasm.FloatUtils (
|
|||||||
doubleToWord
|
doubleToWord
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import Debug.Trace as Debug
|
||||||
|
|
||||||
data Value =
|
data Value =
|
||||||
VI32 Word32
|
VI32 Word32
|
||||||
| VI64 Word64
|
| VI64 Word64
|
||||||
| VF32 Float
|
| VF32 Float
|
||||||
| VF64 Double
|
| VF64 Double
|
||||||
|
| RF (Maybe Natural)
|
||||||
|
| RE (Maybe Natural)
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
asInt32 :: Word32 -> Int32
|
asInt32 :: Word32 -> Int32
|
||||||
@@ -162,9 +172,11 @@ data Label = Label ResultType deriving (Show, Eq)
|
|||||||
|
|
||||||
type Address = Int
|
type Address = Int
|
||||||
|
|
||||||
|
type TableStore = IORef (IOVector (Maybe Address))
|
||||||
|
|
||||||
data TableInstance = TableInstance {
|
data TableInstance = TableInstance {
|
||||||
lim :: Limit,
|
t :: TableType,
|
||||||
elements :: Vector (Maybe Address)
|
items :: TableStore
|
||||||
}
|
}
|
||||||
|
|
||||||
type MemoryStore = ByteArray.MutableByteArray (Primitive.PrimState IO)
|
type MemoryStore = ByteArray.MutableByteArray (Primitive.PrimState IO)
|
||||||
@@ -187,6 +199,8 @@ getValueType (VI32 _) = I32
|
|||||||
getValueType (VI64 _) = I64
|
getValueType (VI64 _) = I64
|
||||||
getValueType (VF32 _) = F32
|
getValueType (VF32 _) = F32
|
||||||
getValueType (VF64 _) = F64
|
getValueType (VF64 _) = F64
|
||||||
|
getValueType (RF _) = Func
|
||||||
|
genValueType (RE _) = Extern
|
||||||
|
|
||||||
data ExportInstance = ExportInstance TL.Text ExternalValue deriving (Eq, Show)
|
data ExportInstance = ExportInstance TL.Text ExternalValue deriving (Eq, Show)
|
||||||
|
|
||||||
@@ -208,11 +222,30 @@ data FunctionInstance =
|
|||||||
hostCode :: HostFunction
|
hostCode :: HostFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data ElemInstance = ElemInstance {
|
||||||
|
eiMode :: ElemMode,
|
||||||
|
eiType :: ElemType,
|
||||||
|
eiItems :: Vector Value,
|
||||||
|
isDropped :: IORef Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
isDeclarative :: ElemMode -> Bool
|
||||||
|
isDeclarative Declarative = True
|
||||||
|
isDeclarative _ = False
|
||||||
|
|
||||||
|
data DataInstance = DataInstance {
|
||||||
|
dMode :: DataMode,
|
||||||
|
isDropped :: IORef Bool,
|
||||||
|
bytes :: LBS.ByteString
|
||||||
|
}
|
||||||
|
|
||||||
data Store = Store {
|
data Store = Store {
|
||||||
funcInstances :: Vector FunctionInstance,
|
funcInstances :: Vector FunctionInstance,
|
||||||
tableInstances :: Vector TableInstance,
|
tableInstances :: Vector TableInstance,
|
||||||
memInstances :: Vector MemoryInstance,
|
memInstances :: Vector MemoryInstance,
|
||||||
globalInstances :: Vector GlobalInstance
|
globalInstances :: Vector GlobalInstance,
|
||||||
|
elemInstances :: Vector ElemInstance,
|
||||||
|
dataInstances :: Vector DataInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyStore :: Store
|
emptyStore :: Store
|
||||||
@@ -220,7 +253,9 @@ emptyStore = Store {
|
|||||||
funcInstances = Vector.empty,
|
funcInstances = Vector.empty,
|
||||||
tableInstances = Vector.empty,
|
tableInstances = Vector.empty,
|
||||||
memInstances = Vector.empty,
|
memInstances = Vector.empty,
|
||||||
globalInstances = Vector.empty
|
globalInstances = Vector.empty,
|
||||||
|
elemInstances = Vector.empty,
|
||||||
|
dataInstances = Vector.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostFunction = [Value] -> IO [Value]
|
type HostFunction = [Value] -> IO [Value]
|
||||||
@@ -284,8 +319,8 @@ makeHostModule st items = do
|
|||||||
makeHostTables :: (Store, ModuleInstance) -> IO (Store, ModuleInstance)
|
makeHostTables :: (Store, ModuleInstance) -> IO (Store, ModuleInstance)
|
||||||
makeHostTables (st, inst) = do
|
makeHostTables (st, inst) = do
|
||||||
let tableLen = Vector.length $ tableInstances st
|
let tableLen = Vector.length $ tableInstances st
|
||||||
let (names, tables) = unzip [(name, Table (TableType lim FuncRef)) | (name, (HostTable lim)) <- items]
|
let (names, tables) = unzip [(name, Table (TableType lim FuncRef)) | (name, HostTable lim) <- items]
|
||||||
let instances = allocTables tables
|
instances <- allocTables tables
|
||||||
let exps = Vector.fromList $ zipWith (\name i -> ExportInstance name (ExternTable i)) names [tableLen..]
|
let exps = Vector.fromList $ zipWith (\name i -> ExportInstance name (ExternTable i)) names [tableLen..]
|
||||||
let inst' = inst {
|
let inst' = inst {
|
||||||
tableaddrs = Vector.fromList [tableLen..tableLen + length instances - 1],
|
tableaddrs = Vector.fromList [tableLen..tableLen + length instances - 1],
|
||||||
@@ -300,6 +335,8 @@ data ModuleInstance = ModuleInstance {
|
|||||||
tableaddrs :: Vector Address,
|
tableaddrs :: Vector Address,
|
||||||
memaddrs :: Vector Address,
|
memaddrs :: Vector Address,
|
||||||
globaladdrs :: Vector Address,
|
globaladdrs :: Vector Address,
|
||||||
|
elemaddrs :: Vector Address,
|
||||||
|
dataaddrs :: Vector Address,
|
||||||
exports :: Vector ExportInstance
|
exports :: Vector ExportInstance
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
@@ -310,15 +347,20 @@ emptyModInstance = ModuleInstance {
|
|||||||
tableaddrs = Vector.empty,
|
tableaddrs = Vector.empty,
|
||||||
memaddrs = Vector.empty,
|
memaddrs = Vector.empty,
|
||||||
globaladdrs = Vector.empty,
|
globaladdrs = Vector.empty,
|
||||||
|
elemaddrs = Vector.empty,
|
||||||
|
dataaddrs = Vector.empty,
|
||||||
exports = Vector.empty
|
exports = Vector.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
calcInstance :: Store -> Imports -> Module -> Initialize ModuleInstance
|
calcInstance :: Store -> Imports -> Module -> Initialize ModuleInstance
|
||||||
calcInstance (Store fs ts ms gs) imps Module {functions, types, tables, mems, globals, exports, imports} = do
|
calcInstance (Store fs ts ms gs es ds) imps mod = do
|
||||||
|
let Module {functions, types, tables, mems, globals, exports, imports, elems, datas} = mod
|
||||||
let funLen = length fs
|
let funLen = length fs
|
||||||
let tableLen = length ts
|
let tableLen = length ts
|
||||||
let memLen = length ms
|
let memLen = length ms
|
||||||
let globalLen = length gs
|
let globalLen = length gs
|
||||||
|
let elemLen = length es
|
||||||
|
let dataLen = length ds
|
||||||
funImps <- mapM checkImportType $ filter isFuncImport imports
|
funImps <- mapM checkImportType $ filter isFuncImport imports
|
||||||
tableImps <- mapM checkImportType $ filter isTableImport imports
|
tableImps <- mapM checkImportType $ filter isTableImport imports
|
||||||
memImps <- mapM checkImportType $ filter isMemImport imports
|
memImps <- mapM checkImportType $ filter isMemImport imports
|
||||||
@@ -342,6 +384,8 @@ calcInstance (Store fs ts ms gs) imps Module {functions, types, tables, mems, gl
|
|||||||
tableaddrs = tbls,
|
tableaddrs = tbls,
|
||||||
memaddrs = memories,
|
memaddrs = memories,
|
||||||
globaladdrs = globs,
|
globaladdrs = globs,
|
||||||
|
elemaddrs = Vector.fromList [elemLen..elemLen + length elems - 1],
|
||||||
|
dataaddrs = Vector.fromList [dataLen..dataLen + length datas - 1],
|
||||||
exports = Vector.fromList $ map refExport exports
|
exports = Vector.fromList $ map refExport exports
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
@@ -357,7 +401,7 @@ calcInstance (Store fs ts ms gs) imps Module {functions, types, tables, mems, gl
|
|||||||
funcAddr <- case idx of
|
funcAddr <- case idx of
|
||||||
ExternFunction funcAddr -> return funcAddr
|
ExternFunction funcAddr -> return funcAddr
|
||||||
other -> throwError "incompatible import type"
|
other -> throwError "incompatible import type"
|
||||||
let expectedType = types !! fromIntegral typeIdx
|
let expectedType = types mod !! fromIntegral typeIdx
|
||||||
let actualType = Language.Wasm.Interpreter.funcType $ fs ! funcAddr
|
let actualType = Language.Wasm.Interpreter.funcType $ fs ! funcAddr
|
||||||
if expectedType == actualType
|
if expectedType == actualType
|
||||||
then return idx
|
then return idx
|
||||||
@@ -379,17 +423,18 @@ calcInstance (Store fs ts ms gs) imps Module {functions, types, tables, mems, gl
|
|||||||
memAddr <- case idx of
|
memAddr <- case idx of
|
||||||
ExternMemory memAddr -> return memAddr
|
ExternMemory memAddr -> return memAddr
|
||||||
_ -> throwError "incompatible import type"
|
_ -> throwError "incompatible import type"
|
||||||
let MemoryInstance { lim } = ms ! memAddr
|
let MemoryInstance { lim = Limit _ limMax, memory = mem } = ms ! memAddr
|
||||||
if limitMatch lim limit
|
size <- liftIO $ (`quot` pageSize) <$> (readIORef mem >>= ByteArray.getSizeofMutableByteArray)
|
||||||
|
if limitMatch (Limit (fromIntegral size) limMax) limit
|
||||||
then return idx
|
then return idx
|
||||||
else throwError "incompatible import type"
|
else throwError "incompatible import type"
|
||||||
checkImportType imp@(Import _ _ (ImportTable (TableType limit _))) = do
|
checkImportType imp@(Import _ _ (ImportTable (TableType limit et))) = do
|
||||||
idx <- getImpIdx imp
|
idx <- getImpIdx imp
|
||||||
tableAddr <- case idx of
|
tableAddr <- case idx of
|
||||||
ExternTable tableAddr -> return tableAddr
|
ExternTable tableAddr -> return tableAddr
|
||||||
_ -> throwError "incompatible import type"
|
_ -> throwError "incompatible import type"
|
||||||
let TableInstance { lim } = ts ! tableAddr
|
let TableInstance { t = TableType lim et' } = ts ! tableAddr
|
||||||
if limitMatch lim limit
|
if limitMatch lim limit && et == et'
|
||||||
then return idx
|
then return idx
|
||||||
else throwError "incompatible import type"
|
else throwError "incompatible import type"
|
||||||
|
|
||||||
@@ -422,6 +467,9 @@ evalConstExpr _ _ [I32Const v] = return $ VI32 v
|
|||||||
evalConstExpr _ _ [I64Const v] = return $ VI64 v
|
evalConstExpr _ _ [I64Const v] = return $ VI64 v
|
||||||
evalConstExpr _ _ [F32Const v] = return $ VF32 v
|
evalConstExpr _ _ [F32Const v] = return $ VF32 v
|
||||||
evalConstExpr _ _ [F64Const v] = return $ VF64 v
|
evalConstExpr _ _ [F64Const v] = return $ VF64 v
|
||||||
|
evalConstExpr _ _ [RefNull FuncRef] = return $ RF Nothing
|
||||||
|
evalConstExpr _ _ [RefNull ExternRef] = return $ RE Nothing
|
||||||
|
evalConstExpr _ _ [RefFunc idx] = return $ RF $ Just idx
|
||||||
evalConstExpr inst store [GetGlobal i] = getGlobalValue inst store i
|
evalConstExpr inst store [GetGlobal i] = getGlobalValue inst store i
|
||||||
evalConstExpr _ _ instrs = error $ "Global initializer contains unsupported instructions: " ++ show instrs
|
evalConstExpr _ _ instrs = error $ "Global initializer contains unsupported instructions: " ++ show instrs
|
||||||
|
|
||||||
@@ -439,15 +487,13 @@ allocAndInitGlobals inst store globs = Vector.fromList <$> mapM allocGlob globs
|
|||||||
val <- runIniter initer
|
val <- runIniter initer
|
||||||
GIMut vt <$> newIORef val
|
GIMut vt <$> newIORef val
|
||||||
|
|
||||||
allocTables :: [Table] -> Vector TableInstance
|
allocTables :: [Table] -> IO (Vector TableInstance)
|
||||||
allocTables tables = Vector.fromList $ map allocTable tables
|
allocTables = fmap Vector.fromList . mapM allocTable
|
||||||
where
|
where
|
||||||
allocTable :: Table -> TableInstance
|
allocTable :: Table -> IO TableInstance
|
||||||
allocTable (Table (TableType lim@(Limit from to) _)) =
|
allocTable (Table t@(TableType lim@(Limit from to) _)) =
|
||||||
TableInstance {
|
let elements = MVector.replicate (fromIntegral from) Nothing in
|
||||||
lim,
|
TableInstance t <$> (elements >>= newIORef)
|
||||||
elements = Vector.fromList $ replicate (fromIntegral from) Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultBudget :: Natural
|
defaultBudget :: Natural
|
||||||
defaultBudget = 300
|
defaultBudget = 300
|
||||||
@@ -469,12 +515,30 @@ allocMems mems = Vector.fromList <$> mapM allocMem mems
|
|||||||
memory
|
memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allocElems :: ModuleInstance -> Store -> [ElemSegment] -> IO (Vector ElemInstance)
|
||||||
|
allocElems inst st = fmap Vector.fromList . mapM allocElem
|
||||||
|
where
|
||||||
|
allocElem :: ElemSegment -> IO ElemInstance
|
||||||
|
allocElem (ElemSegment t mode refs) = do
|
||||||
|
indexes <- flip mapM refs $ \refExpr -> do
|
||||||
|
ref <- evalConstExpr inst st refExpr
|
||||||
|
return $ case ref of
|
||||||
|
RF v -> RF $ fromIntegral . (funcaddrs inst !) . fromIntegral <$> v
|
||||||
|
_ -> ref
|
||||||
|
ElemInstance mode t (Vector.fromList indexes)
|
||||||
|
<$> newIORef False -- is dropped
|
||||||
|
|
||||||
|
allocDatas :: [DataSegment] -> IO (Vector DataInstance)
|
||||||
|
allocDatas datas = Vector.fromList <$> Monad.forM datas (\DataSegment {dataMode, chunk} -> do
|
||||||
|
isDropped <- newIORef False
|
||||||
|
return $ DataInstance dataMode isDropped chunk)
|
||||||
|
|
||||||
type Initialize = ExceptT String (State.StateT Store IO)
|
type Initialize = ExceptT String (State.StateT Store IO)
|
||||||
|
|
||||||
initialize :: ModuleInstance -> Module -> Initialize ()
|
initialize :: ModuleInstance -> Module -> Initialize ()
|
||||||
initialize inst Module {elems, datas, start} = do
|
initialize inst Module {elems, datas, start} = do
|
||||||
checkedMems <- mapM checkData datas
|
checkedMems <- mapM checkData datas
|
||||||
checkedTables <- mapM checkElem elems
|
checkedTables <- mapM checkElem $ filter isActiveElem $ zip [0..] elems
|
||||||
mapM_ initData checkedMems
|
mapM_ initData checkedMems
|
||||||
mapM_ initElem checkedTables
|
mapM_ initElem checkedTables
|
||||||
st <- State.get
|
st <- State.get
|
||||||
@@ -487,55 +551,72 @@ initialize inst Module {elems, datas, start} = do
|
|||||||
_ -> throwError "Start function terminated with trap"
|
_ -> throwError "Start function terminated with trap"
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
where
|
where
|
||||||
checkElem :: ElemSegment -> Initialize (Address, Int, [Address])
|
isActiveElem :: (Int, ElemSegment) -> Bool
|
||||||
checkElem ElemSegment {tableIndex, offset, funcIndexes} = do
|
isActiveElem (_, ElemSegment FuncRef (Active _ _) _) = True
|
||||||
|
isActiveElem _ = False
|
||||||
|
|
||||||
|
checkElem :: (Int, ElemSegment) -> Initialize (Address, Address, Int, [Maybe Address])
|
||||||
|
checkElem (elemN, ElemSegment {elemType, mode, elements}) = do
|
||||||
|
(tableIndex, offset) <- case mode of {
|
||||||
|
Active idx off -> return (idx, off);
|
||||||
|
_ -> throwError "only active mode element can be initialized"
|
||||||
|
}
|
||||||
st <- State.get
|
st <- State.get
|
||||||
VI32 val <- liftIO $ evalConstExpr inst st offset
|
VI32 val <- liftIO $ evalConstExpr inst st offset
|
||||||
let from = fromIntegral val
|
let from = fromIntegral val
|
||||||
let funcs = map ((funcaddrs inst !) . fromIntegral) funcIndexes
|
refs <- liftIO $ mapM (evalConstExpr inst st) elements
|
||||||
|
let funcs = map (\(RF ref) -> (funcaddrs inst !) . fromIntegral <$> ref) refs
|
||||||
let idx = tableaddrs inst ! fromIntegral tableIndex
|
let idx = tableaddrs inst ! fromIntegral tableIndex
|
||||||
let last = from + length funcs
|
return (idx, elemaddrs inst ! elemN, from, funcs)
|
||||||
let TableInstance lim elems = tableInstances st ! idx
|
|
||||||
let len = Vector.length elems
|
|
||||||
Monad.when (last > len) $ throwError "elements segment does not fit"
|
|
||||||
return (idx, from, funcs)
|
|
||||||
|
|
||||||
initElem :: (Address, Int, [Address]) -> Initialize ()
|
initElem :: (Address, Address, Int, [Maybe Address]) -> Initialize ()
|
||||||
initElem (idx, from, funcs) = State.modify $ \st ->
|
initElem (tableIdx, elemIdx, from, funcs) = do
|
||||||
let TableInstance lim elems = tableInstances st ! idx in
|
Store {tableInstances, elemInstances} <- State.get
|
||||||
let table = TableInstance lim (elems // zip [from..] (map Just funcs)) in
|
elems <- liftIO $ readIORef $ items $ tableInstances ! tableIdx
|
||||||
st { tableInstances = tableInstances st Vector.// [(idx, table)] }
|
if from + length funcs > MVector.length elems
|
||||||
|
then throwError "out of bounds table access"
|
||||||
|
else do
|
||||||
|
let ElemInstance {isDropped} = elemInstances ! elemIdx
|
||||||
|
liftIO $ writeIORef isDropped True
|
||||||
|
Monad.forM_ (zip [from..] funcs) $ uncurry $ MVector.unsafeWrite elems
|
||||||
|
|
||||||
checkData :: DataSegment -> Initialize (Int, MemoryStore, LBS.ByteString)
|
checkData :: DataSegment -> Initialize (Maybe (Int, MemoryStore, LBS.ByteString))
|
||||||
checkData DataSegment {memIndex, offset, chunk} = do
|
checkData DataSegment {dataMode = ActiveData memIndex offset, chunk} = do
|
||||||
st <- State.get
|
st <- State.get
|
||||||
VI32 val <- liftIO $ evalConstExpr inst st offset
|
VI32 val <- liftIO $ evalConstExpr inst st offset
|
||||||
let from = fromIntegral val
|
let from = fromIntegral val
|
||||||
let idx = memaddrs inst ! fromIntegral memIndex
|
let idx = memaddrs inst ! fromIntegral memIndex
|
||||||
let last = from + (fromIntegral $ LBS.length chunk)
|
|
||||||
let MemoryInstance _ memory = memInstances st ! idx
|
let MemoryInstance _ memory = memInstances st ! idx
|
||||||
mem <- liftIO $ readIORef memory
|
mem <- liftIO $ readIORef memory
|
||||||
len <- ByteArray.getSizeofMutableByteArray mem
|
return $ Just (from, mem, chunk)
|
||||||
Monad.when (last > len) $ throwError "data segment does not fit"
|
checkData DataSegment {dataMode = PassiveData, chunk} =
|
||||||
return (from, mem, chunk)
|
return Nothing
|
||||||
|
|
||||||
initData :: (Int, MemoryStore, LBS.ByteString) -> Initialize ()
|
initData :: Maybe (Int, MemoryStore, LBS.ByteString) -> Initialize ()
|
||||||
initData (from, mem, chunk) =
|
initData Nothing = return ()
|
||||||
|
initData (Just (from, mem, chunk)) = do
|
||||||
|
let last = from + (fromIntegral $ LBS.length chunk)
|
||||||
|
len <- ByteArray.getSizeofMutableByteArray mem
|
||||||
|
Monad.when (last > len) $ throwError "out of bounds memory access"
|
||||||
mapM_ (\(i,b) -> ByteArray.writeByteArray mem i b) $ zip [from..] $ LBS.unpack chunk
|
mapM_ (\(i,b) -> ByteArray.writeByteArray mem i b) $ zip [from..] $ LBS.unpack chunk
|
||||||
|
|
||||||
instantiate :: Store -> Imports -> Valid.ValidModule -> IO (Either String ModuleInstance, Store)
|
instantiate :: Store -> Imports -> Valid.ValidModule -> IO (Either String ModuleInstance, Store)
|
||||||
instantiate st imps mod = flip State.runStateT st $ runExceptT $ do
|
instantiate st imps mod = flip State.runStateT st $ runExceptT $ do
|
||||||
let m = Valid.getModule mod
|
let m = Valid.getModule mod
|
||||||
inst <- calcInstance st imps m
|
inst <- calcInstance st imps m
|
||||||
let functions = funcInstances st <> (allocFunctions inst $ Struct.functions m)
|
let functions = funcInstances st <> allocFunctions inst (Struct.functions m)
|
||||||
globals <- liftIO $ (globalInstances st <>) <$> (allocAndInitGlobals inst st $ Struct.globals m)
|
globals <- liftIO $ (globalInstances st <>) <$> allocAndInitGlobals inst st (Struct.globals m)
|
||||||
let tables = tableInstances st <> (allocTables $ Struct.tables m)
|
tables <- (tableInstances st <>) <$> liftIO (allocTables (Struct.tables m))
|
||||||
mems <- liftIO $ (memInstances st <>) <$> (allocMems $ Struct.mems m)
|
mems <- liftIO $ (memInstances st <>) <$> allocMems (Struct.mems m)
|
||||||
|
elems <- liftIO $ (elemInstances st <>) <$> allocElems inst st (Struct.elems m)
|
||||||
|
datas <- liftIO $ (dataInstances st <>) <$> allocDatas (Struct.datas m)
|
||||||
State.put $ st {
|
State.put $ st {
|
||||||
funcInstances = functions,
|
funcInstances = functions,
|
||||||
tableInstances = tables,
|
tableInstances = tables,
|
||||||
memInstances = mems,
|
memInstances = mems,
|
||||||
globalInstances = globals
|
globalInstances = globals,
|
||||||
|
elemInstances = elems,
|
||||||
|
dataInstances = datas
|
||||||
}
|
}
|
||||||
initialize inst m
|
initialize inst m
|
||||||
return inst
|
return inst
|
||||||
@@ -579,6 +660,8 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
|
|||||||
checkValType I64 (VI64 v) = Just $ VI64 v
|
checkValType I64 (VI64 v) = Just $ VI64 v
|
||||||
checkValType F32 (VF32 v) = Just $ VF32 v
|
checkValType F32 (VF32 v) = Just $ VF32 v
|
||||||
checkValType F64 (VF64 v) = Just $ VF64 v
|
checkValType F64 (VF64 v) = Just $ VF64 v
|
||||||
|
checkValType Func (RF v) = Just $ RF v
|
||||||
|
checkValType Extern (RE v) = Just $ RE v
|
||||||
checkValType _ _ = Nothing
|
checkValType _ _ = Nothing
|
||||||
|
|
||||||
initLocal :: ValueType -> Value
|
initLocal :: ValueType -> Value
|
||||||
@@ -699,27 +782,42 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
|
|||||||
Just res -> return $ Done ctx { stack = reverse res ++ (drop (length args) $ stack ctx) }
|
Just res -> return $ Done ctx { stack = reverse res ++ (drop (length args) $ stack ctx) }
|
||||||
Nothing -> return Trap
|
Nothing -> return Trap
|
||||||
Nothing -> return Trap
|
Nothing -> return Trap
|
||||||
step ctx@EvalCtx{ stack = (VI32 v): rest } (CallIndirect typeIdx) = do
|
step ctx@EvalCtx{ stack = (VI32 v): rest } (CallIndirect tableIdx typeIdx) = do
|
||||||
let funcType = funcTypes moduleInstance ! fromIntegral typeIdx
|
let funcType = funcTypes moduleInstance ! fromIntegral typeIdx
|
||||||
let TableInstance { elements } = tableInstances store ! (tableaddrs moduleInstance ! 0)
|
let TableInstance { items } = tableInstances store ! (tableaddrs moduleInstance ! fromIntegral tableIdx)
|
||||||
let checks = do
|
let pos = fromIntegral v
|
||||||
addr <- Monad.join $ elements !? fromIntegral v
|
funcs <- readIORef items
|
||||||
let funcInst = funcInstances store ! addr
|
if pos >= MVector.length funcs
|
||||||
let targetType = Language.Wasm.Interpreter.funcType funcInst
|
then return Trap
|
||||||
Monad.guard $ targetType == funcType
|
else do
|
||||||
let args = params targetType
|
maybeAddr <- MVector.unsafeRead funcs pos
|
||||||
Monad.guard $ length args <= length rest
|
let checks = do
|
||||||
params <- sequence $ zipWith checkValType args $ reverse $ take (length args) rest
|
addr <- maybeAddr
|
||||||
return (funcInst, params)
|
let funcInst = funcInstances store ! addr
|
||||||
case checks of
|
let targetType = Language.Wasm.Interpreter.funcType funcInst
|
||||||
Just (funcInst, params) -> do
|
Monad.guard $ targetType == funcType
|
||||||
res <- eval (budget - 1) store funcInst params
|
let args = params targetType
|
||||||
case res of
|
Monad.guard $ length args <= length rest
|
||||||
Just res -> return $ Done ctx { stack = reverse res ++ (drop (length params) rest) }
|
params <- sequence $ zipWith checkValType args $ reverse $ take (length args) rest
|
||||||
Nothing -> return Trap
|
return (funcInst, params)
|
||||||
Nothing -> return Trap
|
case checks of
|
||||||
|
Just (funcInst, params) -> do
|
||||||
|
res <- eval (budget - 1) store funcInst params
|
||||||
|
case res of
|
||||||
|
Just res -> return $ Done ctx { stack = reverse res ++ (drop (length params) rest) }
|
||||||
|
Nothing -> return Trap
|
||||||
|
Nothing -> return Trap
|
||||||
|
step ctx@EvalCtx{ stack = st } (RefNull FuncRef) =
|
||||||
|
return $ Done ctx { stack = RF Nothing : st }
|
||||||
|
step ctx@EvalCtx{ stack = st } (RefNull ExternRef) =
|
||||||
|
return $ Done ctx { stack = RE Nothing : st }
|
||||||
|
step ctx@EvalCtx{ stack = v:rest } RefIsNull =
|
||||||
|
let r = case v of { RE Nothing -> 1; RF Nothing -> 1; _ -> 0 } in
|
||||||
|
return $ Done ctx { stack = VI32 r : rest }
|
||||||
|
step ctx@EvalCtx{ stack = st } (RefFunc index) =
|
||||||
|
return $ Done ctx { stack = RF (Just index) : st }
|
||||||
step ctx@EvalCtx{ stack = (_:rest) } Drop = return $ Done ctx { stack = rest }
|
step ctx@EvalCtx{ stack = (_:rest) } Drop = return $ Done ctx { stack = rest }
|
||||||
step ctx@EvalCtx{ stack = (VI32 test:val2:val1:rest) } Select =
|
step ctx@EvalCtx{ stack = (VI32 test:val2:val1:rest) } (Select _) =
|
||||||
if test == 0
|
if test == 0
|
||||||
then return $ Done ctx { stack = val2 : rest }
|
then return $ Done ctx { stack = val2 : rest }
|
||||||
else return $ Done ctx { stack = val1 : rest }
|
else return $ Done ctx { stack = val1 : rest }
|
||||||
@@ -799,12 +897,12 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
|
|||||||
makeStoreInstr @Word16 ctx { stack = rest } offset 2 $ fromIntegral v
|
makeStoreInstr @Word16 ctx { stack = rest } offset 2 $ fromIntegral v
|
||||||
step ctx@EvalCtx{ stack = (VI64 v:rest) } (I64Store32 MemArg { offset }) =
|
step ctx@EvalCtx{ stack = (VI64 v:rest) } (I64Store32 MemArg { offset }) =
|
||||||
makeStoreInstr @Word32 ctx { stack = rest } offset 4 $ fromIntegral v
|
makeStoreInstr @Word32 ctx { stack = rest } offset 4 $ fromIntegral v
|
||||||
step ctx@EvalCtx{ stack = st } CurrentMemory = do
|
step ctx@EvalCtx{ stack = st } MemorySize = do
|
||||||
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
||||||
memory <- readIORef memoryRef
|
memory <- readIORef memoryRef
|
||||||
size <- ((`quot` pageSize) . fromIntegral) <$> ByteArray.getSizeofMutableByteArray memory
|
size <- ((`quot` pageSize) . fromIntegral) <$> ByteArray.getSizeofMutableByteArray memory
|
||||||
return $ Done ctx { stack = VI32 (fromIntegral size) : st }
|
return $ Done ctx { stack = VI32 (fromIntegral size) : st }
|
||||||
step ctx@EvalCtx{ stack = (VI32 n:rest) } GrowMemory = do
|
step ctx@EvalCtx{ stack = (VI32 n:rest) } MemoryGrow = do
|
||||||
let MemoryInstance { lim = limit@(Limit _ maxLen), memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
let MemoryInstance { lim = limit@(Limit _ maxLen), memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
||||||
memory <- readIORef memoryRef
|
memory <- readIORef memoryRef
|
||||||
size <- (`quot` pageSize) <$> ByteArray.getSizeofMutableByteArray memory
|
size <- (`quot` pageSize) <$> ByteArray.getSizeofMutableByteArray memory
|
||||||
@@ -822,6 +920,161 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
|
|||||||
else return $ -1
|
else return $ -1
|
||||||
)
|
)
|
||||||
return $ Done ctx { stack = VI32 (asWord32 $ fromIntegral result) : rest }
|
return $ Done ctx { stack = VI32 (asWord32 $ fromIntegral result) : rest }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 n:VI32 v:VI32 d:rest) } MemoryFill = do
|
||||||
|
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
||||||
|
memory <- readIORef memoryRef
|
||||||
|
size <- ByteArray.getSizeofMutableByteArray memory
|
||||||
|
let dest = fromIntegral d
|
||||||
|
let len = fromIntegral n
|
||||||
|
if dest + len > size
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
ByteArray.setByteArray @Word8 memory dest len $ fromIntegral v
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 n:VI32 s:VI32 d:rest) } MemoryCopy = do
|
||||||
|
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
||||||
|
memory <- readIORef memoryRef
|
||||||
|
size <- ByteArray.getSizeofMutableByteArray memory
|
||||||
|
let src = fromIntegral s
|
||||||
|
let dest = fromIntegral d
|
||||||
|
let len = fromIntegral n
|
||||||
|
if dest + len > size || src + len > size
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
ByteArray.copyMutableByteArray memory dest memory src len
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 n:VI32 s:VI32 d:rest) } (MemoryInit dataIdx) = do
|
||||||
|
let DataInstance {bytes, isDropped} = dataInstances store ! (dataaddrs moduleInstance ! fromIntegral dataIdx)
|
||||||
|
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
|
||||||
|
memory <- readIORef memoryRef
|
||||||
|
size <- fromIntegral <$> ByteArray.getSizeofMutableByteArray memory
|
||||||
|
let src = fromIntegral s
|
||||||
|
let dest = fromIntegral d
|
||||||
|
let len = fromIntegral n
|
||||||
|
dropped <- readIORef isDropped
|
||||||
|
if (dropped && len > 0) || src + len > LBS.length bytes || dest + len > size
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
mapM_ (uncurry $ ByteArray.writeByteArray memory) $ zip [fromIntegral d..] $
|
||||||
|
LBS.unpack $ LBS.take len $ LBS.drop src bytes
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx (DataDrop dataIdx) = do
|
||||||
|
let DataInstance {isDropped} = dataInstances store ! (dataaddrs moduleInstance ! fromIntegral dataIdx)
|
||||||
|
writeIORef isDropped True
|
||||||
|
return $ Done ctx
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 n:VI32 s:VI32 d:rest) } (TableInit tableIdx elemIdx) = do
|
||||||
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
let TableInstance { items } = tableInstances store ! tableAddr
|
||||||
|
let elemAddr = elemaddrs moduleInstance ! fromIntegral elemIdx
|
||||||
|
let ElemInstance {
|
||||||
|
eiItems = refs,
|
||||||
|
eiMode = mode,
|
||||||
|
isDropped = dropFlag
|
||||||
|
} = elemInstances store ! elemAddr
|
||||||
|
let src = fromIntegral s
|
||||||
|
let dst = fromIntegral d
|
||||||
|
let len = fromIntegral n
|
||||||
|
els <- readIORef items
|
||||||
|
isDropped <- readIORef dropFlag
|
||||||
|
if src + len > Vector.length refs
|
||||||
|
|| dst + len > MVector.length els
|
||||||
|
|| (isDropped && len > 0)
|
||||||
|
|| isDeclarative mode
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
Vector.iforM_ (Vector.slice src len refs) $ \idx (RF fn) -> do
|
||||||
|
MVector.unsafeWrite els (dst + idx) (fromIntegral <$> fn)
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 n:VI32 s:VI32 d:rest) } (TableCopy toIdx fromIdx) = do
|
||||||
|
let fromAddr = tableaddrs moduleInstance ! fromIntegral fromIdx
|
||||||
|
let TableInstance { items = fromItems } = tableInstances store ! fromAddr
|
||||||
|
let toAddr = tableaddrs moduleInstance ! fromIntegral toIdx
|
||||||
|
let TableInstance { items = toItems } = tableInstances store ! toAddr
|
||||||
|
let src = fromIntegral s
|
||||||
|
let dst = fromIntegral d
|
||||||
|
let len = fromIntegral n
|
||||||
|
fromEls <- readIORef fromItems
|
||||||
|
toEls <- readIORef toItems
|
||||||
|
if src + len > MVector.length fromEls || dst + len > MVector.length toEls
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
let range = if dst <= src then [0..len - 1] else reverse [0..len - 1]
|
||||||
|
flip mapM_ range $ \off -> do
|
||||||
|
el <- MVector.unsafeRead fromEls (src + off)
|
||||||
|
MVector.unsafeWrite toEls (dst + off) el
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 n:ref:VI32 i:rest) } (TableFill tableIdx) = do
|
||||||
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
let TableInstance { items, t } = tableInstances store ! tableAddr
|
||||||
|
let TableType (Limit _ max) _ = t
|
||||||
|
let inc = fromIntegral n
|
||||||
|
let from = fromIntegral i
|
||||||
|
let val = case ref of
|
||||||
|
RE extRef -> fromIntegral <$> extRef
|
||||||
|
RF fnRef -> (funcaddrs moduleInstance !) . fromIntegral <$> fnRef
|
||||||
|
v -> error "Impossible due to validation"
|
||||||
|
els <- readIORef items
|
||||||
|
if from + inc > MVector.length els
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
Monad.forM_ [0..inc - 1] $ \off ->
|
||||||
|
MVector.unsafeWrite els (from + off) val
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx@EvalCtx{ stack } (TableSize tableIdx) = do
|
||||||
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
let TableInstance { items } = tableInstances store ! tableAddr
|
||||||
|
len <- MVector.length <$> readIORef items
|
||||||
|
return $ Done ctx { stack = VI32 (fromIntegral len) : stack }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 growBy:ref:rest) } (TableGrow tableIdx) = do
|
||||||
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
let TableInstance { items, t } = tableInstances store ! tableAddr
|
||||||
|
let TableType (Limit _ max) _ = t
|
||||||
|
let inc = fromIntegral growBy
|
||||||
|
let val = case ref of
|
||||||
|
RE extRef -> extRef
|
||||||
|
RF fnRef -> fnRef
|
||||||
|
v -> error "Impossible due to validation"
|
||||||
|
els <- readIORef items
|
||||||
|
let currLen = MVector.length els
|
||||||
|
let newLen = currLen + inc
|
||||||
|
if maybe False ((newLen >) . fromIntegral) max || newLen > 0xFFFFFFFF
|
||||||
|
then return $ Done ctx { stack = VI32 (asWord32 $ -1):rest }
|
||||||
|
else do
|
||||||
|
newEls <- MVector.grow els inc
|
||||||
|
writeIORef items newEls
|
||||||
|
Monad.forM_ [0..inc - 1] $ \off ->
|
||||||
|
MVector.unsafeWrite newEls (currLen + off) (fromIntegral <$> val)
|
||||||
|
return $ Done ctx { stack = VI32 (fromIntegral currLen):rest }
|
||||||
|
step ctx@EvalCtx{ stack = (ref:VI32 offset:rest) } (TableSet tableIdx) = do
|
||||||
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
let TableInstance { items } = tableInstances store ! tableAddr
|
||||||
|
let dst = fromIntegral offset
|
||||||
|
let val = case ref of
|
||||||
|
RE extRef -> fromIntegral <$> extRef
|
||||||
|
RF fnRef -> (funcaddrs moduleInstance !) . fromIntegral <$> fnRef
|
||||||
|
v -> error "Impossible due to validation"
|
||||||
|
els <- readIORef items
|
||||||
|
if dst >= MVector.length els
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
MVector.unsafeWrite els dst val
|
||||||
|
return $ Done ctx { stack = rest }
|
||||||
|
step ctx@EvalCtx{ stack = (VI32 offset:rest) } (TableGet tableIdx) = do
|
||||||
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
let TableInstance { t = TableType _ et, items } = tableInstances store ! tableAddr
|
||||||
|
let dst = fromIntegral offset
|
||||||
|
els <- readIORef items
|
||||||
|
if dst >= MVector.length els
|
||||||
|
then return Trap
|
||||||
|
else do
|
||||||
|
v <- MVector.unsafeRead els dst
|
||||||
|
let val = (case et of {FuncRef -> RF; ExternRef -> RE}) (fromIntegral <$> v)
|
||||||
|
return $ Done ctx { stack = val : rest }
|
||||||
|
step ctx (ElemDrop elemIdx) = do
|
||||||
|
let elemAddr = elemaddrs moduleInstance ! fromIntegral elemIdx
|
||||||
|
let ElemInstance {isDropped} = elemInstances store ! elemAddr
|
||||||
|
writeIORef isDropped True
|
||||||
|
return $ Done ctx
|
||||||
step ctx (I32Const v) = return $ Done ctx { stack = VI32 v : stack ctx }
|
step ctx (I32Const v) = return $ Done ctx { stack = VI32 v : stack ctx }
|
||||||
step ctx (I64Const v) = return $ Done ctx { stack = VI64 v : stack ctx }
|
step ctx (I64Const v) = return $ Done ctx { stack = VI64 v : stack ctx }
|
||||||
step ctx (F32Const v) = return $ Done ctx { stack = VF32 v : stack ctx }
|
step ctx (F32Const v) = return $ Done ctx { stack = VF32 v : stack ctx }
|
||||||
@@ -1207,3 +1460,8 @@ getGlobalValueByName store ModuleInstance { exports } name =
|
|||||||
GIConst _ v -> return v
|
GIConst _ v -> return v
|
||||||
GIMut _ ref -> readIORef ref
|
GIMut _ ref -> readIORef ref
|
||||||
_ -> error $ "Function with name " ++ show name ++ " was not found in module's exports"
|
_ -> error $ "Function with name " ++ show name ++ " was not found in module's exports"
|
||||||
|
|
||||||
|
-- | Retrieve mutable memory from the 'Store'
|
||||||
|
getMemory :: Store -> Address -> Maybe MemoryInstance
|
||||||
|
getMemory Store{memInstances} address = memInstances !? address
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ module Language.Wasm.Lexer (
|
|||||||
Lexeme(..),
|
Lexeme(..),
|
||||||
Token(..),
|
Token(..),
|
||||||
AlexPosn(..),
|
AlexPosn(..),
|
||||||
|
FloatRep(..),
|
||||||
|
NaN(..),
|
||||||
scanner,
|
scanner,
|
||||||
asFloat,
|
asFloat,
|
||||||
asDouble,
|
asDouble,
|
||||||
@@ -22,6 +24,8 @@ 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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+357
-98
@@ -93,6 +93,8 @@ import Language.Wasm.Lexer (
|
|||||||
),
|
),
|
||||||
Lexeme(..),
|
Lexeme(..),
|
||||||
AlexPosn(..),
|
AlexPosn(..),
|
||||||
|
FloatRep(..),
|
||||||
|
NaN(..),
|
||||||
asFloat,
|
asFloat,
|
||||||
asDouble,
|
asDouble,
|
||||||
doubleFromInteger
|
doubleFromInteger
|
||||||
@@ -119,6 +121,8 @@ import Language.Wasm.Lexer (
|
|||||||
'f64' { Lexeme _ (TKeyword "f64") }
|
'f64' { Lexeme _ (TKeyword "f64") }
|
||||||
'mut' { Lexeme _ (TKeyword "mut") }
|
'mut' { Lexeme _ (TKeyword "mut") }
|
||||||
'funcref' { Lexeme _ (TKeyword "funcref") }
|
'funcref' { Lexeme _ (TKeyword "funcref") }
|
||||||
|
'externref' { Lexeme _ (TKeyword "externref") }
|
||||||
|
'extern' { Lexeme _ (TKeyword "extern") }
|
||||||
'type' { Lexeme _ (TKeyword "type") }
|
'type' { Lexeme _ (TKeyword "type") }
|
||||||
'unreachable' { Lexeme _ (TKeyword "unreachable") }
|
'unreachable' { Lexeme _ (TKeyword "unreachable") }
|
||||||
'nop' { Lexeme _ (TKeyword "nop") }
|
'nop' { Lexeme _ (TKeyword "nop") }
|
||||||
@@ -128,6 +132,10 @@ import Language.Wasm.Lexer (
|
|||||||
'return' { Lexeme _ (TKeyword "return") }
|
'return' { Lexeme _ (TKeyword "return") }
|
||||||
'call' { Lexeme _ (TKeyword "call") }
|
'call' { Lexeme _ (TKeyword "call") }
|
||||||
'call_indirect' { Lexeme _ (TKeyword "call_indirect") }
|
'call_indirect' { Lexeme _ (TKeyword "call_indirect") }
|
||||||
|
'ref.null' { Lexeme _ (TKeyword "ref.null") }
|
||||||
|
'ref.is_null' { Lexeme _ (TKeyword "ref.is_null") }
|
||||||
|
'ref.func' { Lexeme _ (TKeyword "ref.func") }
|
||||||
|
'ref.extern' { Lexeme _ (TKeyword "ref.extern") }
|
||||||
'drop' { Lexeme _ (TKeyword "drop") }
|
'drop' { Lexeme _ (TKeyword "drop") }
|
||||||
'select' { Lexeme _ (TKeyword "select") }
|
'select' { Lexeme _ (TKeyword "select") }
|
||||||
'get_local' { Lexeme _ (TKeyword "local.get") }
|
'get_local' { Lexeme _ (TKeyword "local.get") }
|
||||||
@@ -160,6 +168,18 @@ import Language.Wasm.Lexer (
|
|||||||
'i64.store32' { Lexeme _ (TKeyword "i64.store32") }
|
'i64.store32' { Lexeme _ (TKeyword "i64.store32") }
|
||||||
'memory.size' { Lexeme _ (TKeyword "memory.size") }
|
'memory.size' { Lexeme _ (TKeyword "memory.size") }
|
||||||
'memory.grow' { Lexeme _ (TKeyword "memory.grow") }
|
'memory.grow' { Lexeme _ (TKeyword "memory.grow") }
|
||||||
|
'memory.fill' { Lexeme _ (TKeyword "memory.fill") }
|
||||||
|
'memory.copy' { Lexeme _ (TKeyword "memory.copy") }
|
||||||
|
'memory.init' { Lexeme _ (TKeyword "memory.init") }
|
||||||
|
'data.drop' { Lexeme _ (TKeyword "data.drop") }
|
||||||
|
'table.init' { Lexeme _ (TKeyword "table.init") }
|
||||||
|
'table.copy' { Lexeme _ (TKeyword "table.copy") }
|
||||||
|
'table.fill' { Lexeme _ (TKeyword "table.fill") }
|
||||||
|
'table.size' { Lexeme _ (TKeyword "table.size") }
|
||||||
|
'table.grow' { Lexeme _ (TKeyword "table.grow") }
|
||||||
|
'table.get' { Lexeme _ (TKeyword "table.get") }
|
||||||
|
'table.set' { Lexeme _ (TKeyword "table.set") }
|
||||||
|
'elem.drop' { Lexeme _ (TKeyword "elem.drop") }
|
||||||
'i32.const' { Lexeme _ (TKeyword "i32.const") }
|
'i32.const' { Lexeme _ (TKeyword "i32.const") }
|
||||||
'i64.const' { Lexeme _ (TKeyword "i64.const") }
|
'i64.const' { Lexeme _ (TKeyword "i64.const") }
|
||||||
'f32.const' { Lexeme _ (TKeyword "f32.const") }
|
'f32.const' { Lexeme _ (TKeyword "f32.const") }
|
||||||
@@ -313,6 +333,8 @@ import Language.Wasm.Lexer (
|
|||||||
'export' { Lexeme _ (TKeyword "export") }
|
'export' { Lexeme _ (TKeyword "export") }
|
||||||
'local' { Lexeme _ (TKeyword "local") }
|
'local' { Lexeme _ (TKeyword "local") }
|
||||||
'elem' { Lexeme _ (TKeyword "elem") }
|
'elem' { Lexeme _ (TKeyword "elem") }
|
||||||
|
'item' { Lexeme _ (TKeyword "item") }
|
||||||
|
'declare' { Lexeme _ (TKeyword "declare") }
|
||||||
'data' { Lexeme _ (TKeyword "data") }
|
'data' { Lexeme _ (TKeyword "data") }
|
||||||
'offset' { Lexeme _ (TKeyword "offset") }
|
'offset' { Lexeme _ (TKeyword "offset") }
|
||||||
'start' { Lexeme _ (TKeyword "start") }
|
'start' { Lexeme _ (TKeyword "start") }
|
||||||
@@ -363,6 +385,8 @@ valtype :: { ValueType }
|
|||||||
| 'i64' { I64 }
|
| 'i64' { I64 }
|
||||||
| 'f32' { F32 }
|
| 'f32' { F32 }
|
||||||
| 'f64' { F64 }
|
| 'f64' { F64 }
|
||||||
|
| 'funcref' { Func }
|
||||||
|
| 'externref' { Extern }
|
||||||
|
|
||||||
index :: { Index }
|
index :: { Index }
|
||||||
: u32 { Index $1 }
|
: u32 { Index $1 }
|
||||||
@@ -389,23 +413,23 @@ int64 :: { Integer }
|
|||||||
else Left ("Int literal value is out of signed int64 boundaries: " ++ show $1)
|
else Left ("Int literal value is out of signed int64 boundaries: " ++ show $1)
|
||||||
}
|
}
|
||||||
|
|
||||||
float32 :: { Float }
|
float32 :: { FloatRep }
|
||||||
: int {%
|
: int {%
|
||||||
let maxInt = 340282356779733623858607532500980858880 in
|
let maxInt = 340282356779733623858607532500980858880 in
|
||||||
if $1 <= maxInt && $1 >= -maxInt
|
if $1 <= maxInt && $1 >= -maxInt
|
||||||
then return $ fromIntegral $1
|
then return $ BinRep $ fromIntegral $1
|
||||||
else Left "constant out of range"
|
else Left "constant out of range"
|
||||||
}
|
}
|
||||||
| f64 {% asFloat $1 }
|
| f64 { $1 }
|
||||||
|
|
||||||
float64 :: { Double }
|
float64 :: { FloatRep }
|
||||||
: int {%
|
: int {%
|
||||||
let maxInt = round (maxFinite :: Double) in
|
let maxInt = round (maxFinite :: Double) in
|
||||||
if $1 <= maxInt && $1 >= -maxInt
|
if $1 <= maxInt && $1 >= -maxInt
|
||||||
then doubleFromInteger $1
|
then fmap BinRep $ doubleFromInteger $1
|
||||||
else Left "constant out of range"
|
else Left "constant out of range"
|
||||||
}
|
}
|
||||||
| f64 {% asDouble $1 }
|
| f64 { $1 }
|
||||||
|
|
||||||
plaininstr :: { PlainInstr }
|
plaininstr :: { PlainInstr }
|
||||||
-- control instructions
|
-- control instructions
|
||||||
@@ -417,7 +441,11 @@ plaininstr :: { PlainInstr }
|
|||||||
| 'return' { Return }
|
| 'return' { Return }
|
||||||
| 'call' index { Call $2 }
|
| 'call' index { Call $2 }
|
||||||
| 'drop' { Drop }
|
| 'drop' { Drop }
|
||||||
| 'select' { Select }
|
-- reference instructions
|
||||||
|
| 'ref.null' heaptype { RefNull $2 }
|
||||||
|
| 'ref.is_null' { RefIsNull }
|
||||||
|
| 'ref.func' index { RefFunc $2 }
|
||||||
|
| 'ref.extern' u32 { RefExtern $2 }
|
||||||
-- variable instructions
|
-- variable instructions
|
||||||
| 'get_local' index { GetLocal $2 }
|
| 'get_local' index { GetLocal $2 }
|
||||||
| 'set_local' index { SetLocal $2 }
|
| 'set_local' index { SetLocal $2 }
|
||||||
@@ -448,8 +476,25 @@ plaininstr :: { PlainInstr }
|
|||||||
| 'i64.store8' memarg1 { I64Store8 $2 }
|
| 'i64.store8' memarg1 { I64Store8 $2 }
|
||||||
| 'i64.store16' memarg2 { I64Store16 $2 }
|
| 'i64.store16' memarg2 { I64Store16 $2 }
|
||||||
| 'i64.store32' memarg4 { I64Store32 $2 }
|
| 'i64.store32' memarg4 { I64Store32 $2 }
|
||||||
| 'memory.size' { CurrentMemory }
|
| 'memory.size' { MemorySize }
|
||||||
| 'memory.grow' { GrowMemory }
|
| 'memory.grow' { MemoryGrow }
|
||||||
|
| 'memory.fill' { MemoryFill }
|
||||||
|
| 'memory.copy' { MemoryCopy }
|
||||||
|
| 'memory.init' index { MemoryInit $2 }
|
||||||
|
| 'data.drop' index { DataDrop $2 }
|
||||||
|
-- table instructions
|
||||||
|
| 'table.init' index opt(index) {
|
||||||
|
case $3 of
|
||||||
|
Nothing -> TableInit (Index 0) $2
|
||||||
|
Just elemIdx -> TableInit $2 elemIdx
|
||||||
|
}
|
||||||
|
| 'table.get' opt(index) { TableGet (fromMaybe (Index 0) $2) }
|
||||||
|
| 'table.set' opt(index) { TableSet (fromMaybe (Index 0) $2) }
|
||||||
|
| 'table.copy' opt(index) opt(index) { TableCopy (fromMaybe (Index 0) $2) (fromMaybe (Index 0) $3) }
|
||||||
|
| 'table.fill' opt(index) { TableFill (fromMaybe (Index 0) $2) }
|
||||||
|
| 'table.size' opt(index) { TableSize (fromMaybe (Index 0) $2) }
|
||||||
|
| 'table.grow' opt(index) { TableGrow (fromMaybe (Index 0) $2) }
|
||||||
|
| 'elem.drop' index { ElemDrop $2 }
|
||||||
-- numeric instructions
|
-- numeric instructions
|
||||||
| 'i32.const' int32 { I32Const $2 }
|
| 'i32.const' int32 { I32Const $2 }
|
||||||
| 'i64.const' int64 { I64Const $2 }
|
| 'i64.const' int64 { I64Const $2 }
|
||||||
@@ -657,12 +702,30 @@ memarg4 :: { MemArg }
|
|||||||
memarg8 :: { MemArg }
|
memarg8 :: { MemArg }
|
||||||
: opt(offset) opt(align) {% parseMemArg 8 $1 $2 }
|
: opt(offset) opt(align) {% parseMemArg 8 $1 $2 }
|
||||||
|
|
||||||
|
select_type_or_instructions(terminator)
|
||||||
|
: terminator { ($1, Nothing, []) }
|
||||||
|
| '(' select_type_or_instructions1(terminator) { $2 }
|
||||||
|
|
||||||
|
select_type_or_instructions1(terminator)
|
||||||
|
: 'result' list(valtype) ')' mixed_instruction_list(terminator) {
|
||||||
|
let (end, instr) = $4 in
|
||||||
|
(end, Just $2, instr)
|
||||||
|
}
|
||||||
|
| folded_instr_list(terminator) {
|
||||||
|
let (end, instr) = $1 in
|
||||||
|
(end, Nothing, instr)
|
||||||
|
}
|
||||||
|
|
||||||
instruction_list(terminator)
|
instruction_list(terminator)
|
||||||
: terminator { ($1, []) }
|
: terminator { ($1, []) }
|
||||||
| plaininstr mixed_instruction_list(terminator) { ([PlainInstr $1] ++) `fmap` $2 }
|
| plaininstr mixed_instruction_list(terminator) { ([PlainInstr $1] ++) `fmap` $2 }
|
||||||
| 'call_indirect' typeuse(terminator) {%
|
| 'call_indirect' opt(index) typeuse(terminator) {%
|
||||||
let (tu, instr, end) = $2 in
|
let tableIdx = fromMaybe (Index 0) $2 in
|
||||||
onlyAnonimParams tu >> (return (end, [PlainInstr $ CallIndirect tu] ++ instr))
|
let (tu, instr, end) = $3 in
|
||||||
|
onlyAnonimParams tu >> (return (end, [PlainInstr $ CallIndirect tableIdx tu] ++ instr))
|
||||||
|
}
|
||||||
|
| 'select' select_type_or_instructions(terminator) {
|
||||||
|
let (end, t, instr) = $2 in (end, [PlainInstr $ Select t] ++ instr)
|
||||||
}
|
}
|
||||||
| 'block' opt(ident) typeuse('end') opt(ident) mixed_instruction_list(terminator) {% do
|
| 'block' opt(ident) typeuse('end') opt(ident) mixed_instruction_list(terminator) {% do
|
||||||
let (tu, instr, _) = $3
|
let (tu, instr, _) = $3
|
||||||
@@ -700,9 +763,13 @@ folded_instr :: { [Instruction] }
|
|||||||
|
|
||||||
folded_instr1 :: { [Instruction] }
|
folded_instr1 :: { [Instruction] }
|
||||||
: plaininstr mixed_instruction_list(')') { snd $2 ++ [PlainInstr $1] }
|
: plaininstr mixed_instruction_list(')') { snd $2 ++ [PlainInstr $1] }
|
||||||
| 'call_indirect' typeuse(')') {%
|
| 'call_indirect' opt(index) typeuse(')') {%
|
||||||
let (tu, instr, _) = $2 in
|
let tableIdx = fromMaybe (Index 0) $2 in
|
||||||
onlyAnonimParams tu >> (return $ instr ++ [PlainInstr $ CallIndirect tu])
|
let (tu, instr, _) = $3 in
|
||||||
|
onlyAnonimParams tu >> (return $ instr ++ [PlainInstr $ CallIndirect tableIdx tu])
|
||||||
|
}
|
||||||
|
| 'select' select_type_or_instructions(')') {
|
||||||
|
let (_, t, instr) = $2 in instr ++ [PlainInstr $ Select t]
|
||||||
}
|
}
|
||||||
| 'block' opt(ident) typeuse(')') {%
|
| 'block' opt(ident) typeuse(')') {%
|
||||||
let (typeUse, instr, _) = $3 in
|
let (typeUse, instr, _) = $3 in
|
||||||
@@ -839,9 +906,12 @@ memory_limits_export_import1 :: { Maybe Ident -> [ModuleField] }
|
|||||||
| 'data' datastring ')' ')' {
|
| 'data' datastring ')' ')' {
|
||||||
\ident ->
|
\ident ->
|
||||||
let m = fromIntegral $ LBS.length $2 in
|
let m = fromIntegral $ LBS.length $2 in
|
||||||
|
let lim = if m `mod` 0x10000 == 0 then m `div` 0x10000 else m `div` 0x10000 + 1 in
|
||||||
|
-- TODO: unhardcode memory index
|
||||||
|
let memIdx = fromMaybe (Index 0) $ Named `fmap` ident in
|
||||||
[
|
[
|
||||||
MFMem $ Memory [] ident $ Limit m $ Just m,
|
MFMem $ Memory [] ident $ Limit lim $ Just lim,
|
||||||
MFData $ DataSegment (fromMaybe (Index 0) $ Named `fmap` ident) [PlainInstr $ I32Const 0] $2
|
MFData $ DataSegment Nothing (ActiveData memIdx [PlainInstr $ I32Const 0]) $2
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,6 +926,11 @@ limits :: { Limit }
|
|||||||
|
|
||||||
elemtype :: { ElemType }
|
elemtype :: { ElemType }
|
||||||
: 'funcref' { FuncRef }
|
: 'funcref' { FuncRef }
|
||||||
|
| 'externref' { ExternRef }
|
||||||
|
|
||||||
|
heaptype :: { ElemType }
|
||||||
|
: 'func' { FuncRef }
|
||||||
|
| 'extern' { ExternRef }
|
||||||
|
|
||||||
tabletype :: { TableType }
|
tabletype :: { TableType }
|
||||||
: limits elemtype { TableType $1 $2 }
|
: limits elemtype { TableType $1 $2 }
|
||||||
@@ -865,15 +940,24 @@ table :: { [ModuleField] }
|
|||||||
|
|
||||||
limits_elemtype_elem :: { Maybe Ident -> [ModuleField] }
|
limits_elemtype_elem :: { Maybe Ident -> [ModuleField] }
|
||||||
: tabletype ')' { \ident -> [MFTable $ Table [] ident $1] }
|
: tabletype ')' { \ident -> [MFTable $ Table [] ident $1] }
|
||||||
| elemtype '(' 'elem' list(index) ')' ')' {
|
| elemtype '(' 'elem' indexes_or_ref_exprs ')' ')' {
|
||||||
\ident ->
|
\ident ->
|
||||||
let funcsLen = fromIntegral $ length $4 in [
|
let funcsLen = fromIntegral $ length $4 in [
|
||||||
MFTable $ Table [] ident $ TableType (Limit funcsLen (Just funcsLen)) $1,
|
MFTable $ Table [] ident $ TableType (Limit funcsLen (Just funcsLen)) $1,
|
||||||
MFElem $ ElemSegment (fromMaybe (Index 0) $ Named `fmap` ident) [PlainInstr $ I32Const 0] $4
|
-- TODO: unhardcode table index
|
||||||
|
let tableIndex = (fromMaybe (Index 0) $ Named `fmap` ident) in
|
||||||
|
let offset = [PlainInstr $ I32Const 0] in
|
||||||
|
let elements = $4 in
|
||||||
|
MFElem $ ElemSegment Nothing FuncRef (Active tableIndex offset) elements
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
| '(' import_export_table { $2 }
|
| '(' import_export_table { $2 }
|
||||||
|
|
||||||
|
indexes_or_ref_exprs :: {[[Instruction]]}
|
||||||
|
: index list(index) { funcIndexToExpr ($1:$2) }
|
||||||
|
| elemexpr list(elemexpr) { $1 : $2 }
|
||||||
|
| {- empty -} { [] }
|
||||||
|
|
||||||
import_export_table :: { Maybe Ident -> [ModuleField] }
|
import_export_table :: { Maybe Ident -> [ModuleField] }
|
||||||
: 'import' name name ')' tabletype ')' {
|
: 'import' name name ')' tabletype ')' {
|
||||||
\ident -> [MFImport $ Import [] $2 $3 $ ImportTable ident $5]
|
\ident -> [MFImport $ Import [] $2 $3 $ ImportTable ident $5]
|
||||||
@@ -900,25 +984,58 @@ export :: { Export }
|
|||||||
start :: { StartFunction }
|
start :: { StartFunction }
|
||||||
: 'start' index ')' { StartFunction $2 }
|
: 'start' index ')' { StartFunction $2 }
|
||||||
|
|
||||||
-- TODO: Spec from 09 Jan 2018 declares 'offset' keyword as mandatory,
|
elem :: { ElemSegment }
|
||||||
-- but collection of testcases omits 'offset' in this position
|
: 'elem' opt(ident) elem1 { $3{ ident = $2 } }
|
||||||
-- I am going to support both options for now, but maybe it has to be updated in future.
|
|
||||||
offsetexpr :: { [Instruction] }
|
elem1 :: { ElemSegment }
|
||||||
|
: elemlist ')' { let (t, els) = $1 in ElemSegment Nothing t Passive els }
|
||||||
|
| 'declare' elemlist ')' { let (t, els) = $2 in ElemSegment Nothing t Declarative els }
|
||||||
|
| '(' elem1_active ')' { $2 }
|
||||||
|
|
||||||
|
elem1_active :: { ElemSegment }
|
||||||
|
: 'table' index ')' '(' elem1_active_offset {
|
||||||
|
let (offset, t, els) = $5 in ElemSegment Nothing t (Active $2 offset) els
|
||||||
|
}
|
||||||
|
| elem1_active_offset {
|
||||||
|
let (offset, t, els) = $1 in ElemSegment Nothing t (Active (Index 0) offset) els
|
||||||
|
}
|
||||||
|
|
||||||
|
elem1_active_offset :: { ([Instruction], ElemType, [[Instruction]]) }
|
||||||
|
: 'offset' mixed_instruction_list(')') elemlist { (snd $2, fst $3, snd $3) }
|
||||||
|
| folded_instr1 elemlist { ($1, fst $2, snd $2) }
|
||||||
|
|
||||||
|
elemlist :: { (ElemType, [[Instruction]]) }
|
||||||
|
: 'func' list(index) { (FuncRef, funcIndexToExpr $2) }
|
||||||
|
| 'funcref' list(elemexpr) { (FuncRef, $2) }
|
||||||
|
| 'externref' { (ExternRef, []) }
|
||||||
|
| list(index) { (FuncRef, funcIndexToExpr $1) }
|
||||||
|
|
||||||
|
elemexpr :: { [Instruction] }
|
||||||
|
: plaininstr { [PlainInstr $1] }
|
||||||
|
| '(' 'item' mixed_instruction_list(')') { snd $3 }
|
||||||
|
| '(' folded_instr1 { $2 }
|
||||||
|
|
||||||
|
offsetexpr1 :: { [Instruction] }
|
||||||
: 'offset' mixed_instruction_list(')') { snd $2 }
|
: 'offset' mixed_instruction_list(')') { snd $2 }
|
||||||
| folded_instr1 { $1 }
|
| folded_instr1 { $1 }
|
||||||
|
|
||||||
elemsegment :: { ElemSegment }
|
memory_offsetexpr1 :: { (MemoryIndex, [Instruction]) }
|
||||||
: 'elem' opt(index) '(' offsetexpr list(index) ')' { ElemSegment (fromMaybe (Index 0) $2) $4 $5 }
|
: offsetexpr1 { (Index 0, $1)}
|
||||||
|
| 'memory' index ')' '(' offsetexpr1 { ($2, $5) }
|
||||||
|
|
||||||
|
memory_mode :: { DataMode }
|
||||||
|
: '(' memory_offsetexpr1 { uncurry ActiveData $2 }
|
||||||
|
| {- empty -} { PassiveData }
|
||||||
|
|
||||||
datasegment :: { DataSegment }
|
datasegment :: { DataSegment }
|
||||||
: 'data' opt(index) '(' offsetexpr datastring ')' { DataSegment (fromMaybe (Index 0) $2) $4 $5 }
|
: 'data' opt(ident) memory_mode datastring ')' { DataSegment $2 $3 $4 }
|
||||||
|
|
||||||
modulefield1_single :: { ModuleField }
|
modulefield1_single :: { ModuleField }
|
||||||
: typedef { MFType $1 }
|
: typedef { MFType $1 }
|
||||||
| import { MFImport $1 }
|
| import { MFImport $1 }
|
||||||
| export { MFExport $1 }
|
| export { MFExport $1 }
|
||||||
| start { MFStart $1 }
|
| start { MFStart $1 }
|
||||||
| elemsegment { MFElem $1 }
|
| elem { MFElem $1 }
|
||||||
| datasegment { MFData $1 }
|
| datasegment { MFData $1 }
|
||||||
| function { $1 }
|
| function { $1 }
|
||||||
| global { $1 }
|
| global { $1 }
|
||||||
@@ -964,11 +1081,15 @@ module1 :: { ModuleDef }
|
|||||||
| 'module' opt(ident) list(modulefield) ')' {% RawModDef $2 `fmap` (desugarize $ concat $3) }
|
| 'module' opt(ident) list(modulefield) ')' {% RawModDef $2 `fmap` (desugarize $ concat $3) }
|
||||||
|
|
||||||
action1 :: { Action }
|
action1 :: { Action }
|
||||||
: 'invoke' opt(ident) string list(folded_instr) ')' { Invoke $2 $3 (map (map constInstructionToValue) $4) }
|
: 'invoke' opt(ident) string list(folded_instr) ')' {%
|
||||||
|
fmap (Invoke $2 $3) $ (mapM (mapM constInstructionToValue) $4)
|
||||||
|
}
|
||||||
| 'get' opt(ident) string ')' { Get $2 $3 }
|
| 'get' opt(ident) string ')' { Get $2 $3 }
|
||||||
|
|
||||||
assertion1 :: { (Maybe AlexPosn, Assertion) }
|
assertion1 :: { (Maybe AlexPosn, Assertion) }
|
||||||
: 'assert_return' '(' action1 list(folded_instr) ')' { ($1, AssertReturn $3 (map (map constInstructionToValue) $4)) }
|
: 'assert_return' '(' action1 list(folded_instr) ')' {%
|
||||||
|
fmap ((\a -> ($1, a)) . AssertReturn $3) $ (mapM (mapM constInstructionToValue) $4)
|
||||||
|
}
|
||||||
| 'assert_return_canonical_nan' '(' action1 ')' { ($1, AssertReturnCanonicalNaN $3) }
|
| 'assert_return_canonical_nan' '(' action1 ')' { ($1, AssertReturnCanonicalNaN $3) }
|
||||||
| 'assert_return_arithmetic_nan' '(' action1 ')' { ($1, AssertReturnArithmeticNaN $3) }
|
| 'assert_return_arithmetic_nan' '(' action1 ')' { ($1, AssertReturnArithmeticNaN $3) }
|
||||||
| 'assert_trap' '(' assertion_trap string ')' { ($1, AssertTrap $3 $4) }
|
| 'assert_trap' '(' assertion_trap string ')' { ($1, AssertTrap $3 $4) }
|
||||||
@@ -1084,7 +1205,7 @@ integerToWord64 i
|
|||||||
| i < 0 && i >= -(2 ^ 63) = 0xFFFFFFFFFFFFFFFF - (fromIntegral (abs i)) + 1
|
| i < 0 && i >= -(2 ^ 63) = 0xFFFFFFFFFFFFFFFF - (fromIntegral (abs i)) + 1
|
||||||
| otherwise = error "I64 is out of bounds."
|
| otherwise = error "I64 is out of bounds."
|
||||||
|
|
||||||
data FuncType = FuncType { params :: [ParamType], results :: [ValueType] } deriving (Show, Eq, Generic, NFData)
|
data FuncType = FuncType { params :: [ParamType], results :: [ValueType] } deriving (Show, Eq)
|
||||||
|
|
||||||
emptyFuncType :: FuncType
|
emptyFuncType :: FuncType
|
||||||
emptyFuncType = FuncType [] []
|
emptyFuncType = FuncType [] []
|
||||||
@@ -1092,11 +1213,11 @@ emptyFuncType = FuncType [] []
|
|||||||
data ParamType = ParamType {
|
data ParamType = ParamType {
|
||||||
ident :: Maybe Ident,
|
ident :: Maybe Ident,
|
||||||
paramType :: ValueType
|
paramType :: ValueType
|
||||||
} deriving (Show, Eq, Generic, NFData)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
newtype Ident = Ident TL.Text deriving (Show, Eq, Generic, NFData)
|
newtype Ident = Ident TL.Text deriving (Show, Eq)
|
||||||
|
|
||||||
data Index = Named Ident | Index Natural deriving (Show, Eq, Generic, NFData)
|
data Index = Named Ident | Index Natural deriving (Show, Eq)
|
||||||
|
|
||||||
type LabelIndex = Index
|
type LabelIndex = Index
|
||||||
type FuncIndex = Index
|
type FuncIndex = Index
|
||||||
@@ -1105,6 +1226,8 @@ type LocalIndex = Index
|
|||||||
type GlobalIndex = Index
|
type GlobalIndex = Index
|
||||||
type TableIndex = Index
|
type TableIndex = Index
|
||||||
type MemoryIndex = Index
|
type MemoryIndex = Index
|
||||||
|
type ElemIndex = Index
|
||||||
|
type DataIndex = Index
|
||||||
|
|
||||||
data PlainInstr =
|
data PlainInstr =
|
||||||
-- Control instructions
|
-- Control instructions
|
||||||
@@ -1115,10 +1238,15 @@ data PlainInstr =
|
|||||||
| BrTable [LabelIndex] LabelIndex
|
| BrTable [LabelIndex] LabelIndex
|
||||||
| Return
|
| Return
|
||||||
| Call FuncIndex
|
| Call FuncIndex
|
||||||
| CallIndirect TypeUse
|
| CallIndirect TableIndex TypeUse
|
||||||
|
-- Reference instructions
|
||||||
|
| RefNull ElemType
|
||||||
|
| RefIsNull
|
||||||
|
| RefFunc FuncIndex
|
||||||
|
| RefExtern Natural
|
||||||
-- Parametric instructions
|
-- Parametric instructions
|
||||||
| Drop
|
| Drop
|
||||||
| Select
|
| Select (Maybe [ValueType])
|
||||||
-- Variable instructions
|
-- Variable instructions
|
||||||
| GetLocal LocalIndex
|
| GetLocal LocalIndex
|
||||||
| SetLocal LocalIndex
|
| SetLocal LocalIndex
|
||||||
@@ -1149,13 +1277,26 @@ data PlainInstr =
|
|||||||
| I64Store8 MemArg
|
| I64Store8 MemArg
|
||||||
| I64Store16 MemArg
|
| I64Store16 MemArg
|
||||||
| I64Store32 MemArg
|
| I64Store32 MemArg
|
||||||
| CurrentMemory
|
| MemorySize
|
||||||
| GrowMemory
|
| MemoryGrow
|
||||||
|
| MemoryFill
|
||||||
|
| MemoryCopy
|
||||||
|
| MemoryInit DataIndex
|
||||||
|
| DataDrop DataIndex
|
||||||
|
-- Table instructions
|
||||||
|
| TableInit TableIndex ElemIndex
|
||||||
|
| TableGrow TableIndex
|
||||||
|
| TableSize TableIndex
|
||||||
|
| TableFill TableIndex
|
||||||
|
| TableGet TableIndex
|
||||||
|
| TableSet TableIndex
|
||||||
|
| TableCopy TableIndex TableIndex
|
||||||
|
| ElemDrop ElemIndex
|
||||||
-- Numeric instructions
|
-- Numeric instructions
|
||||||
| I32Const Integer
|
| I32Const Integer
|
||||||
| I64Const Integer
|
| I64Const Integer
|
||||||
| F32Const Float
|
| F32Const FloatRep
|
||||||
| F64Const Double
|
| F64Const FloatRep
|
||||||
| IUnOp BitSize IUnOp
|
| IUnOp BitSize IUnOp
|
||||||
| IBinOp BitSize IBinOp
|
| IBinOp BitSize IBinOp
|
||||||
| I32Eqz
|
| I32Eqz
|
||||||
@@ -1177,14 +1318,14 @@ data PlainInstr =
|
|||||||
| F64PromoteF32
|
| F64PromoteF32
|
||||||
| IReinterpretF BitSize
|
| IReinterpretF BitSize
|
||||||
| FReinterpretI BitSize
|
| FReinterpretI BitSize
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data TypeDef = TypeDef (Maybe Ident) FuncType deriving (Show, Eq, Generic, NFData)
|
data TypeDef = TypeDef (Maybe Ident) FuncType deriving (Show, Eq)
|
||||||
|
|
||||||
data TypeUse =
|
data TypeUse =
|
||||||
IndexedTypeUse TypeIndex (Maybe FuncType)
|
IndexedTypeUse TypeIndex (Maybe FuncType)
|
||||||
| AnonimousTypeUse FuncType
|
| AnonimousTypeUse FuncType
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
emptyTypeUse = AnonimousTypeUse emptyFuncType
|
emptyTypeUse = AnonimousTypeUse emptyFuncType
|
||||||
|
|
||||||
@@ -1206,26 +1347,26 @@ data Instruction =
|
|||||||
trueBranch :: [Instruction],
|
trueBranch :: [Instruction],
|
||||||
falseBranch :: [Instruction]
|
falseBranch :: [Instruction]
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data Import = Import {
|
data Import = Import {
|
||||||
reExportAs :: [TL.Text],
|
reExportAs :: [TL.Text],
|
||||||
sourceModule :: TL.Text,
|
sourceModule :: TL.Text,
|
||||||
name :: TL.Text,
|
name :: TL.Text,
|
||||||
desc :: ImportDesc
|
desc :: ImportDesc
|
||||||
} deriving (Show, Eq, Generic, NFData)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
data ImportDesc =
|
data ImportDesc =
|
||||||
ImportFunc (Maybe Ident) TypeUse
|
ImportFunc (Maybe Ident) TypeUse
|
||||||
| ImportTable (Maybe Ident) TableType
|
| ImportTable (Maybe Ident) TableType
|
||||||
| ImportMemory (Maybe Ident) Limit
|
| ImportMemory (Maybe Ident) Limit
|
||||||
| ImportGlobal (Maybe Ident) GlobalType
|
| ImportGlobal (Maybe Ident) GlobalType
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data LocalType = LocalType {
|
data LocalType = LocalType {
|
||||||
ident :: Maybe Ident,
|
ident :: Maybe Ident,
|
||||||
localType :: ValueType
|
localType :: ValueType
|
||||||
} deriving (Show, Eq, Generic, NFData)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
data Function = Function {
|
data Function = Function {
|
||||||
exportFuncAs :: [TL.Text],
|
exportFuncAs :: [TL.Text],
|
||||||
@@ -1234,7 +1375,7 @@ data Function = Function {
|
|||||||
locals :: [LocalType],
|
locals :: [LocalType],
|
||||||
body :: [Instruction]
|
body :: [Instruction]
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
emptyFunction :: Function
|
emptyFunction :: Function
|
||||||
emptyFunction =
|
emptyFunction =
|
||||||
@@ -1252,40 +1393,52 @@ data Global = Global {
|
|||||||
globalType :: GlobalType,
|
globalType :: GlobalType,
|
||||||
initializer :: [Instruction]
|
initializer :: [Instruction]
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data Memory = Memory [TL.Text] (Maybe Ident) Limit deriving (Show, Eq, Generic, NFData)
|
data Memory = Memory [TL.Text] (Maybe Ident) Limit deriving (Show, Eq)
|
||||||
|
|
||||||
data Table = Table [TL.Text] (Maybe Ident) TableType deriving (Show, Eq, Generic, NFData)
|
data Table = Table [TL.Text] (Maybe Ident) TableType deriving (Show, Eq)
|
||||||
|
|
||||||
data ExportDesc =
|
data ExportDesc =
|
||||||
ExportFunc FuncIndex
|
ExportFunc FuncIndex
|
||||||
| ExportTable TableIndex
|
| ExportTable TableIndex
|
||||||
| ExportMemory MemoryIndex
|
| ExportMemory MemoryIndex
|
||||||
| ExportGlobal GlobalIndex
|
| ExportGlobal GlobalIndex
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data Export = Export {
|
data Export = Export {
|
||||||
name :: TL.Text,
|
name :: TL.Text,
|
||||||
desc :: ExportDesc
|
desc :: ExportDesc
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data StartFunction = StartFunction FuncIndex deriving (Show, Eq, Generic, NFData)
|
data StartFunction = StartFunction FuncIndex deriving (Show, Eq)
|
||||||
|
|
||||||
|
data ElemMode
|
||||||
|
= Passive
|
||||||
|
| Active TableIndex [Instruction]
|
||||||
|
| Declarative
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data ElemSegment = ElemSegment {
|
data ElemSegment = ElemSegment {
|
||||||
tableIndex :: TableIndex,
|
ident :: Maybe Ident,
|
||||||
offset :: [Instruction],
|
elemType :: ElemType,
|
||||||
funcIndexes :: [FuncIndex]
|
mode :: ElemMode,
|
||||||
|
elements :: [[Instruction]]
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
data DataMode =
|
||||||
|
PassiveData
|
||||||
|
| ActiveData MemoryIndex [Instruction]
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data DataSegment = DataSegment {
|
data DataSegment = DataSegment {
|
||||||
memIndex :: MemoryIndex,
|
ident :: Maybe Ident,
|
||||||
offset :: [Instruction],
|
dataMode :: DataMode,
|
||||||
datastring :: LBS.ByteString
|
datastring :: LBS.ByteString
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data ModuleField =
|
data ModuleField =
|
||||||
MFType TypeDef
|
MFType TypeDef
|
||||||
@@ -1298,7 +1451,7 @@ data ModuleField =
|
|||||||
| MFStart StartFunction
|
| MFStart StartFunction
|
||||||
| MFElem ElemSegment
|
| MFElem ElemSegment
|
||||||
| MFData DataSegment
|
| MFData DataSegment
|
||||||
deriving(Show, Eq, Generic, NFData)
|
deriving(Show, Eq)
|
||||||
|
|
||||||
happyError (Lexeme _ EOF : []) = Left $ "Error occuried during parsing phase at the end of file"
|
happyError (Lexeme _ EOF : []) = Left $ "Error occuried during parsing phase at the end of file"
|
||||||
happyError (Lexeme Nothing tok : tokens) = Left $ "Error occuried during parsing phase at the end of file"
|
happyError (Lexeme Nothing tok : tokens) = Left $ "Error occuried during parsing phase at the end of file"
|
||||||
@@ -1371,12 +1524,17 @@ data FunCtx = FunCtx {
|
|||||||
ctxParams :: [ParamType]
|
ctxParams :: [ParamType]
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
constInstructionToValue :: Instruction -> S.Instruction Natural
|
constInstructionToValue :: Instruction -> Either String (S.Instruction Natural)
|
||||||
constInstructionToValue (PlainInstr (I32Const v)) = S.I32Const $ integerToWord32 v
|
constInstructionToValue (PlainInstr (I32Const v)) = return $ S.I32Const $ integerToWord32 v
|
||||||
constInstructionToValue (PlainInstr (F32Const v)) = S.F32Const v
|
constInstructionToValue (PlainInstr (F32Const v)) = S.F32Const <$> asFloat v
|
||||||
constInstructionToValue (PlainInstr (I64Const v)) = S.I64Const $ integerToWord64 v
|
constInstructionToValue (PlainInstr (I64Const v)) = return $ S.I64Const $ integerToWord64 v
|
||||||
constInstructionToValue (PlainInstr (F64Const v)) = S.F64Const v
|
constInstructionToValue (PlainInstr (F64Const v)) = S.F64Const <$> asDouble v
|
||||||
constInstructionToValue _ = error "Only const instructions supported as arguments for actions"
|
constInstructionToValue (PlainInstr (RefNull et)) = return $ S.RefNull et
|
||||||
|
constInstructionToValue (PlainInstr (RefExtern n)) = return $ S.RefExtern n
|
||||||
|
constInstructionToValue _ = Left "Only const instructions supported as arguments for actions"
|
||||||
|
|
||||||
|
funcIndexToExpr :: [FuncIndex] -> [[Instruction]]
|
||||||
|
funcIndexToExpr = map $ (:[]) . PlainInstr . RefFunc
|
||||||
|
|
||||||
desugarize :: [ModuleField] -> Either String S.Module
|
desugarize :: [ModuleField] -> Either String S.Module
|
||||||
desugarize fields = do
|
desugarize fields = do
|
||||||
@@ -1463,17 +1621,13 @@ desugarize fields = do
|
|||||||
extractTypeDefFromInstructions (matchTypeUse defs funcType) body
|
extractTypeDefFromInstructions (matchTypeUse defs funcType) body
|
||||||
extractTypeDef defs (MFGlobal Global { initializer }) =
|
extractTypeDef defs (MFGlobal Global { initializer }) =
|
||||||
extractTypeDefFromInstructions defs initializer
|
extractTypeDefFromInstructions defs initializer
|
||||||
extractTypeDef defs (MFElem ElemSegment { offset }) =
|
|
||||||
extractTypeDefFromInstructions defs offset
|
|
||||||
extractTypeDef defs (MFData DataSegment { offset }) =
|
|
||||||
extractTypeDefFromInstructions defs offset
|
|
||||||
extractTypeDef defs _ = defs
|
extractTypeDef defs _ = defs
|
||||||
|
|
||||||
extractTypeDefFromInstructions :: [TypeDef] -> [Instruction] -> [TypeDef]
|
extractTypeDefFromInstructions :: [TypeDef] -> [Instruction] -> [TypeDef]
|
||||||
extractTypeDefFromInstructions = foldl' extractTypeDefFromInstruction
|
extractTypeDefFromInstructions = foldl' extractTypeDefFromInstruction
|
||||||
|
|
||||||
extractTypeDefFromInstruction :: [TypeDef] -> Instruction -> [TypeDef]
|
extractTypeDefFromInstruction :: [TypeDef] -> Instruction -> [TypeDef]
|
||||||
extractTypeDefFromInstruction defs (PlainInstr (CallIndirect typeUse)) =
|
extractTypeDefFromInstruction defs (PlainInstr (CallIndirect _ typeUse)) =
|
||||||
matchTypeUse defs typeUse
|
matchTypeUse defs typeUse
|
||||||
extractTypeDefFromInstruction defs (BlockInstr { body, blockType }) =
|
extractTypeDefFromInstruction defs (BlockInstr { body, blockType }) =
|
||||||
extractTypeDefFromInstructions (matchTypeUse defs blockType) body
|
extractTypeDefFromInstructions (matchTypeUse defs blockType) body
|
||||||
@@ -1556,12 +1710,23 @@ desugarize fields = do
|
|||||||
case getFuncIndex ctxMod funIdx of
|
case getFuncIndex ctxMod funIdx of
|
||||||
Just idx -> return $ S.Call idx
|
Just idx -> return $ S.Call idx
|
||||||
Nothing -> Left "unknown function"
|
Nothing -> Left "unknown function"
|
||||||
synInstrToStruct FunCtx { ctxMod = Module { types } } (PlainInstr (CallIndirect typeUse)) =
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (CallIndirect tableIdx typeUse)) =
|
||||||
case getTypeIndex types typeUse of
|
case getTableIndex ctxMod tableIdx of
|
||||||
Just idx -> return $ S.CallIndirect idx
|
Just tableIdx ->
|
||||||
Nothing -> Left "unknown type"
|
case getTypeIndex (types ctxMod) typeUse of
|
||||||
|
Just idx -> return $ S.CallIndirect tableIdx idx
|
||||||
|
Nothing -> Left "unknown type"
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
synInstrToStruct _ (PlainInstr Drop) = return $ S.Drop
|
synInstrToStruct _ (PlainInstr Drop) = return $ S.Drop
|
||||||
synInstrToStruct _ (PlainInstr Select) = return $ S.Select
|
synInstrToStruct _ (PlainInstr (Select vt)) = return $ S.Select vt
|
||||||
|
synInstrToStruct _ (PlainInstr (RefNull elType)) = return $ S.RefNull elType
|
||||||
|
synInstrToStruct _ (PlainInstr RefIsNull) = return $ S.RefIsNull
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (RefFunc funIdx)) =
|
||||||
|
case getFuncIndex ctxMod funIdx of
|
||||||
|
Just idx -> return $ S.RefFunc idx
|
||||||
|
Nothing -> Left "unknown function"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (RefExtern idx)) =
|
||||||
|
return $ S.RefExtern idx
|
||||||
synInstrToStruct ctx (PlainInstr (GetLocal localIdx)) =
|
synInstrToStruct ctx (PlainInstr (GetLocal localIdx)) =
|
||||||
case getLocalIndex ctx localIdx of
|
case getLocalIndex ctx localIdx of
|
||||||
Just idx -> return $ S.GetLocal idx
|
Just idx -> return $ S.GetLocal idx
|
||||||
@@ -1605,12 +1770,68 @@ desugarize fields = do
|
|||||||
synInstrToStruct _ (PlainInstr (I64Store8 memArg)) = return $ S.I64Store8 memArg
|
synInstrToStruct _ (PlainInstr (I64Store8 memArg)) = return $ S.I64Store8 memArg
|
||||||
synInstrToStruct _ (PlainInstr (I64Store16 memArg)) = return $ S.I64Store16 memArg
|
synInstrToStruct _ (PlainInstr (I64Store16 memArg)) = return $ S.I64Store16 memArg
|
||||||
synInstrToStruct _ (PlainInstr (I64Store32 memArg)) = return $ S.I64Store32 memArg
|
synInstrToStruct _ (PlainInstr (I64Store32 memArg)) = return $ S.I64Store32 memArg
|
||||||
synInstrToStruct _ (PlainInstr CurrentMemory) = return $ S.CurrentMemory
|
synInstrToStruct _ (PlainInstr MemorySize) = return $ S.MemorySize
|
||||||
synInstrToStruct _ (PlainInstr GrowMemory) = return $ S.GrowMemory
|
synInstrToStruct _ (PlainInstr MemoryGrow) = return $ S.MemoryGrow
|
||||||
|
synInstrToStruct _ (PlainInstr MemoryFill) = return $ S.MemoryFill
|
||||||
|
synInstrToStruct _ (PlainInstr MemoryCopy) = return $ S.MemoryCopy
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (MemoryInit dataIdx)) =
|
||||||
|
case getDataIndex ctxMod dataIdx of
|
||||||
|
Just dataIdx -> return $ S.MemoryInit dataIdx
|
||||||
|
Nothing -> Left "unknown data"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (DataDrop dataIdx)) =
|
||||||
|
case getDataIndex ctxMod dataIdx of
|
||||||
|
Just dataIdx -> return $ S.DataDrop dataIdx
|
||||||
|
Nothing -> Left "unknown data"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableInit tableIdx elemIdx)) =
|
||||||
|
case getTableIndex ctxMod tableIdx of
|
||||||
|
Just tableIdx ->
|
||||||
|
case getElemIndex ctxMod elemIdx of
|
||||||
|
Just elemIdx -> return $ S.TableInit tableIdx elemIdx
|
||||||
|
Nothing -> Left "unknown elem"
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableCopy toIdx fromIdx)) =
|
||||||
|
case getTableIndex ctxMod fromIdx of
|
||||||
|
Just fromIdx ->
|
||||||
|
case getTableIndex ctxMod toIdx of
|
||||||
|
Just toIdx -> return $ S.TableCopy toIdx fromIdx
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableSize tableIdx)) =
|
||||||
|
case getTableIndex ctxMod tableIdx of
|
||||||
|
Just tableIdx -> return $ S.TableSize tableIdx
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableFill tableIdx)) =
|
||||||
|
case getTableIndex ctxMod tableIdx of
|
||||||
|
Just tableIdx -> return $ S.TableFill tableIdx
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableGrow tableIdx)) =
|
||||||
|
case getTableIndex ctxMod tableIdx of
|
||||||
|
Just tableIdx -> return $ S.TableGrow tableIdx
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableSet tableIdx)) =
|
||||||
|
case getTableIndex ctxMod tableIdx of
|
||||||
|
Just tableIdx -> return $ S.TableSet tableIdx
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (TableGet tableIdx)) =
|
||||||
|
case getTableIndex ctxMod tableIdx of
|
||||||
|
Just tableIdx -> return $ S.TableGet tableIdx
|
||||||
|
Nothing -> Left "unknown table"
|
||||||
|
synInstrToStruct FunCtx { ctxMod } (PlainInstr (ElemDrop elemIdx)) =
|
||||||
|
case getElemIndex ctxMod elemIdx of
|
||||||
|
Just elemIdx -> return $ S.ElemDrop elemIdx
|
||||||
|
Nothing -> Left "unknown elem"
|
||||||
synInstrToStruct _ (PlainInstr (I32Const val)) = return $ S.I32Const $ integerToWord32 val
|
synInstrToStruct _ (PlainInstr (I32Const val)) = return $ S.I32Const $ integerToWord32 val
|
||||||
synInstrToStruct _ (PlainInstr (I64Const val)) = return $ S.I64Const $ integerToWord64 val
|
synInstrToStruct _ (PlainInstr (I64Const val)) = return $ S.I64Const $ integerToWord64 val
|
||||||
synInstrToStruct _ (PlainInstr (F32Const val)) = return $ S.F32Const val
|
synInstrToStruct _ (PlainInstr (F32Const (NanRep Arithmetic))) =
|
||||||
synInstrToStruct _ (PlainInstr (F64Const val)) = return $ S.F64Const val
|
Left "arithmetic nan constant allowed only in script"
|
||||||
|
synInstrToStruct _ (PlainInstr (F32Const (NanRep Canonical))) =
|
||||||
|
Left "canonical nan constant allowed only in script"
|
||||||
|
synInstrToStruct _ (PlainInstr (F32Const rep)) = S.F32Const <$> asFloat rep
|
||||||
|
synInstrToStruct _ (PlainInstr (F64Const (NanRep Arithmetic))) =
|
||||||
|
Left "arithmetic nan constant allowed only in script"
|
||||||
|
synInstrToStruct _ (PlainInstr (F64Const (NanRep Canonical))) =
|
||||||
|
Left "canonical nan constant allowed only in script"
|
||||||
|
synInstrToStruct _ (PlainInstr (F64Const rep)) = S.F64Const <$> asDouble rep
|
||||||
synInstrToStruct _ (PlainInstr (IUnOp sz op)) = return $ S.IUnOp sz op
|
synInstrToStruct _ (PlainInstr (IUnOp sz op)) = return $ S.IUnOp sz op
|
||||||
synInstrToStruct _ (PlainInstr (IBinOp sz op)) = return $ S.IBinOp sz op
|
synInstrToStruct _ (PlainInstr (IBinOp sz op)) = return $ S.IBinOp sz op
|
||||||
synInstrToStruct _ (PlainInstr I32Eqz) = return $ S.I32Eqz
|
synInstrToStruct _ (PlainInstr I32Eqz) = return $ S.I32Eqz
|
||||||
@@ -1878,29 +2099,59 @@ desugarize fields = do
|
|||||||
|
|
||||||
-- elem segment
|
-- elem segment
|
||||||
synElemToStruct :: Module -> ElemSegment -> Either String S.ElemSegment
|
synElemToStruct :: Module -> ElemSegment -> Either String S.ElemSegment
|
||||||
synElemToStruct mod ElemSegment { tableIndex, offset, funcIndexes } =
|
synElemToStruct mod ElemSegment { ident, elemType, mode, elements } = do
|
||||||
let ctx = FunCtx mod [] [] [] in
|
let ctx = FunCtx mod [] [] []
|
||||||
let offsetInstrs = mapM (synInstrToStruct ctx) offset in
|
m <- case mode of {
|
||||||
let idx = fromJust $ getTableIndex mod tableIndex in
|
Active tableIndex offset ->
|
||||||
let indexes = map (fromJust . getFuncIndex mod) funcIndexes in
|
let offsetInstrs = mapM (synInstrToStruct ctx) offset in
|
||||||
S.ElemSegment idx <$> offsetInstrs <*> return indexes
|
let idx = fromJust $ getTableIndex mod tableIndex in
|
||||||
|
S.Active idx <$> offsetInstrs;
|
||||||
|
Passive -> return S.Passive;
|
||||||
|
Declarative -> return S.Declarative
|
||||||
|
}
|
||||||
|
let elemExprs = mapM (mapM (synInstrToStruct ctx)) elements
|
||||||
|
S.ElemSegment elemType m <$> elemExprs
|
||||||
|
|
||||||
extractElemSegment :: [ElemSegment] -> ModuleField -> [ElemSegment]
|
extractElemSegment :: [ElemSegment] -> ModuleField -> [ElemSegment]
|
||||||
extractElemSegment elems (MFElem elem) = elem : elems
|
extractElemSegment elems (MFElem elem) = elem : elems
|
||||||
extractElemSegment elems _ = elems
|
extractElemSegment elems _ = elems
|
||||||
|
|
||||||
|
getElemIndex :: Module -> GlobalIndex -> Maybe Natural
|
||||||
|
getElemIndex mod@Module { elems } (Named id) =
|
||||||
|
let isIdent (_, ElemSegment { ident }) = ident == Just id in
|
||||||
|
let elemIndexes = map fst $ filter isIdent $ zip [0..] elems in
|
||||||
|
case elemIndexes of
|
||||||
|
[idx] -> return idx
|
||||||
|
_ -> Nothing
|
||||||
|
getElemIndex _ (Index idx) = Just idx
|
||||||
|
|
||||||
-- data segment
|
-- data segment
|
||||||
synDataToStruct :: Module -> DataSegment -> Either String S.DataSegment
|
synDataToStruct :: Module -> DataSegment -> Either String S.DataSegment
|
||||||
synDataToStruct mod DataSegment { memIndex, offset, datastring } =
|
synDataToStruct mod DataSegment { dataMode, datastring } = do
|
||||||
let ctx = FunCtx mod [] [] [] in
|
m <- case dataMode of
|
||||||
let offsetInstrs = mapM (synInstrToStruct ctx) offset in
|
PassiveData -> return S.PassiveData
|
||||||
let idx = fromJust $ getMemIndex mod memIndex in
|
ActiveData memIndex offset -> do
|
||||||
S.DataSegment idx <$> offsetInstrs <*> return datastring
|
let ctx = FunCtx mod [] [] []
|
||||||
|
offsetInstrs <- mapM (synInstrToStruct ctx) offset
|
||||||
|
idx <- case getMemIndex mod memIndex of
|
||||||
|
Just idx -> return idx
|
||||||
|
Nothing -> throwError "unknown memory"
|
||||||
|
return $ S.ActiveData idx offsetInstrs
|
||||||
|
return $ S.DataSegment m datastring
|
||||||
|
|
||||||
extractDataSegment :: [DataSegment] -> ModuleField -> [DataSegment]
|
extractDataSegment :: [DataSegment] -> ModuleField -> [DataSegment]
|
||||||
extractDataSegment datas (MFData dataSegment) = dataSegment : datas
|
extractDataSegment datas (MFData dataSegment) = dataSegment : datas
|
||||||
extractDataSegment datas _ = datas
|
extractDataSegment datas _ = datas
|
||||||
|
|
||||||
|
getDataIndex :: Module -> GlobalIndex -> Maybe Natural
|
||||||
|
getDataIndex mod@Module { datas } (Named id) =
|
||||||
|
let isIdent (_, DataSegment { ident }) = ident == Just id in
|
||||||
|
let dataIndexes = map fst $ filter isIdent $ zip [0..] datas in
|
||||||
|
case dataIndexes of
|
||||||
|
[idx] -> return idx
|
||||||
|
_ -> Nothing
|
||||||
|
getDataIndex _ (Index idx) = Just idx
|
||||||
|
|
||||||
-- start
|
-- start
|
||||||
synStartToStruct :: Module -> StartFunction -> S.StartFunction
|
synStartToStruct :: Module -> StartFunction -> S.StartFunction
|
||||||
synStartToStruct mod (StartFunction funIdx) =
|
synStartToStruct mod (StartFunction funIdx) =
|
||||||
@@ -1916,14 +2167,22 @@ desugarize fields = do
|
|||||||
-- exports
|
-- exports
|
||||||
extractExports :: Module -> [ModuleField] -> [ModuleField]
|
extractExports :: Module -> [ModuleField] -> [ModuleField]
|
||||||
extractExports mod mf =
|
extractExports mod mf =
|
||||||
let initial = (funcImportLength, globImportLength, memImportLength, tableImportLength, []) in
|
let fromImports = foldl' reexport (0, 0, 0, 0, []) $ imports mod in
|
||||||
let (_, _, _, _, result) = foldl' extractExport initial mf in
|
let (_, _, _, _, result) = foldl' extractExport fromImports mf in
|
||||||
reverse result
|
reverse result
|
||||||
where
|
where
|
||||||
funcImportLength = fromIntegral $ length $ filter isFuncImport $ imports mod
|
reexport (fidx, gidx, midx, tidx, mf) (Import {reExportAs, desc = ImportFunc _ _}) =
|
||||||
globImportLength = fromIntegral $ length $ filter isGlobalImport $ imports mod
|
let exports = map (\name -> MFExport $ Export name $ ExportFunc $ Index fidx) reExportAs in
|
||||||
memImportLength = fromIntegral $ length $ filter isMemImport $ imports mod
|
(fidx + 1, gidx, midx, tidx, exports ++ mf)
|
||||||
tableImportLength = fromIntegral $ length $ filter isTableImport $ imports mod
|
reexport (fidx, gidx, midx, tidx, mf) (Import {reExportAs, desc = ImportGlobal _ _}) =
|
||||||
|
let exports = map (\name -> MFExport $ Export name $ ExportGlobal $ Index gidx) reExportAs in
|
||||||
|
(fidx, gidx + 1, midx, tidx, exports ++ mf)
|
||||||
|
reexport (fidx, gidx, midx, tidx, mf) (Import {reExportAs, desc = ImportMemory _ _}) =
|
||||||
|
let exports = map (\name -> MFExport $ Export name $ ExportMemory $ Index midx) reExportAs in
|
||||||
|
(fidx, gidx, midx + 1, tidx, exports ++ mf)
|
||||||
|
reexport (fidx, gidx, midx, tidx, mf) (Import {reExportAs, desc = ImportTable _ _}) =
|
||||||
|
let exports = map (\name -> MFExport $ Export name $ ExportTable $ Index tidx) reExportAs in
|
||||||
|
(fidx, gidx, midx, tidx + 1, exports ++ mf)
|
||||||
|
|
||||||
extractExport (fidx, gidx, midx, tidx, mf) (MFFunc fun@Function{ exportFuncAs }) =
|
extractExport (fidx, gidx, midx, tidx, mf) (MFFunc fun@Function{ exportFuncAs }) =
|
||||||
let exports = map (\name -> MFExport $ Export name $ ExportFunc $ Index fidx) exportFuncAs in
|
let exports = map (\name -> MFExport $ Export name $ ExportFunc $ Index fidx) exportFuncAs in
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Control.Monad.IO.Class (liftIO)
|
|||||||
import Numeric.IEEE (identicalIEEE)
|
import Numeric.IEEE (identicalIEEE)
|
||||||
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(..),
|
||||||
@@ -122,7 +123,10 @@ runScript onAssertFail script = do
|
|||||||
asArg [Struct.F32Const v] = Interpreter.VF32 v
|
asArg [Struct.F32Const v] = Interpreter.VF32 v
|
||||||
asArg [Struct.I64Const v] = Interpreter.VI64 v
|
asArg [Struct.I64Const v] = Interpreter.VI64 v
|
||||||
asArg [Struct.F64Const v] = Interpreter.VF64 v
|
asArg [Struct.F64Const v] = Interpreter.VF64 v
|
||||||
asArg _ = error "Only const instructions supported as arguments for actions"
|
asArg [Struct.RefNull Struct.FuncRef] = Interpreter.RF Nothing
|
||||||
|
asArg [Struct.RefNull Struct.ExternRef] = Interpreter.RE Nothing
|
||||||
|
asArg [Struct.RefExtern v] = Interpreter.RE (Just v)
|
||||||
|
asArg expr = error $ "Only const instructions supported as arguments for actions: " ++ show expr
|
||||||
|
|
||||||
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
|
||||||
@@ -139,6 +143,8 @@ runScript onAssertFail script = do
|
|||||||
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) = (isNaN v1 && isNaN 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) = (isNaN v1 && isNaN v2) || identicalIEEE v1 v2
|
isValueEqual (Interpreter.VF64 v1) (Interpreter.VF64 v2) = (isNaN v1 && isNaN v2) || identicalIEEE v1 v2
|
||||||
|
isValueEqual (Interpreter.RF f1) (Interpreter.RF f2) = f1 == f2
|
||||||
|
isValueEqual (Interpreter.RE e1) (Interpreter.RE e2) = e1 == e2
|
||||||
isValueEqual _ _ = False
|
isValueEqual _ _ = False
|
||||||
|
|
||||||
isNaNReturned :: Action -> Assertion -> AssertM ()
|
isNaNReturned :: Action -> Assertion -> AssertM ()
|
||||||
@@ -161,7 +167,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 ()
|
||||||
@@ -169,13 +175,13 @@ 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 = ["unknown function", "unknown function 0"]
|
getFailureString (Validate.FunctionIndexOutOfRange idx) = ["unknown function", "unknown function " <> TL.pack (show idx)]
|
||||||
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.TypeIndexOutOfRange = ["unknown type"]
|
getFailureString Validate.TypeIndexOutOfRange = ["unknown type"]
|
||||||
@@ -188,7 +194,10 @@ 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 r = [TL.concat ["not implemented ", (TL.pack $ show r)]]
|
getFailureString (Validate.ElemIndexOutOfRange idx) = ["unknown elem segment " <> TL.pack (show idx)]
|
||||||
|
getFailureString (Validate.DataIndexOutOfRange idx) = ["unknown data segment", "unknown data segment " <> TL.pack (show idx)]
|
||||||
|
getFailureString (Validate.UndeclaredFunctionRef _) = ["undeclared function reference"]
|
||||||
|
getFailureString r = [TL.concat ["not implemented ", TL.pack $ show r]]
|
||||||
|
|
||||||
printFailedAssert :: String -> Assertion -> AssertM ()
|
printFailedAssert :: String -> Assertion -> AssertM ()
|
||||||
printFailedAssert msg assert = do
|
printFailedAssert msg assert = do
|
||||||
@@ -253,12 +262,14 @@ 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 <- fst <$> State.get
|
(st, pos) <- 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)
|
||||||
_ -> printFailedAssert ("Module linking should fail with trap during execution of a start function") assert
|
r -> 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
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
module Language.Wasm.Structure (
|
module Language.Wasm.Structure (
|
||||||
Module(..),
|
Module(..),
|
||||||
|
DataMode(..),
|
||||||
DataSegment(..),
|
DataSegment(..),
|
||||||
ElemSegment(..),
|
ElemSegment(..),
|
||||||
|
ElemMode(..),
|
||||||
StartFunction(..),
|
StartFunction(..),
|
||||||
Export(..),
|
Export(..),
|
||||||
ExportDesc(..),
|
ExportDesc(..),
|
||||||
@@ -102,12 +104,16 @@ 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
|
||||||
|
| Func
|
||||||
|
| Extern
|
||||||
deriving (Show, Eq, Generic, NFData)
|
deriving (Show, Eq, Generic, NFData)
|
||||||
|
|
||||||
type ResultType = [ValueType]
|
type ResultType = [ValueType]
|
||||||
@@ -133,10 +139,15 @@ data Instruction index =
|
|||||||
| BrTable [index] index
|
| BrTable [index] index
|
||||||
| Return
|
| Return
|
||||||
| Call index
|
| Call index
|
||||||
| CallIndirect index
|
| CallIndirect index index
|
||||||
|
-- Reference instructions
|
||||||
|
| RefNull ElemType
|
||||||
|
| RefIsNull
|
||||||
|
| RefFunc index
|
||||||
|
| RefExtern Natural
|
||||||
-- Parametric instructions
|
-- Parametric instructions
|
||||||
| Drop
|
| Drop
|
||||||
| Select
|
| Select (Maybe [ValueType])
|
||||||
-- Variable instructions
|
-- Variable instructions
|
||||||
| GetLocal index
|
| GetLocal index
|
||||||
| SetLocal index
|
| SetLocal index
|
||||||
@@ -167,8 +178,21 @@ data Instruction index =
|
|||||||
| I64Store8 MemArg
|
| I64Store8 MemArg
|
||||||
| I64Store16 MemArg
|
| I64Store16 MemArg
|
||||||
| I64Store32 MemArg
|
| I64Store32 MemArg
|
||||||
| CurrentMemory
|
| MemorySize
|
||||||
| GrowMemory
|
| MemoryGrow
|
||||||
|
| MemoryFill
|
||||||
|
| MemoryCopy
|
||||||
|
| MemoryInit DataIndex
|
||||||
|
| DataDrop DataIndex
|
||||||
|
-- Table instructions
|
||||||
|
| TableInit TableIndex ElemIndex
|
||||||
|
| TableGrow TableIndex
|
||||||
|
| TableSize TableIndex
|
||||||
|
| TableFill TableIndex
|
||||||
|
| TableGet TableIndex
|
||||||
|
| TableSet TableIndex
|
||||||
|
| TableCopy TableIndex TableIndex
|
||||||
|
| ElemDrop ElemIndex
|
||||||
-- Numeric instructions
|
-- Numeric instructions
|
||||||
| I32Const Word32
|
| I32Const Word32
|
||||||
| I64Const Word64
|
| I64Const Word64
|
||||||
@@ -207,7 +231,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 deriving (Show, Eq, Generic, NFData)
|
data ElemType = FuncRef | ExternRef 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)
|
||||||
|
|
||||||
@@ -222,15 +246,25 @@ 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 {
|
||||||
tableIndex :: TableIndex,
|
elemType :: ElemType,
|
||||||
offset :: Expression,
|
mode :: ElemMode,
|
||||||
funcIndexes :: [FuncIndex]
|
elements :: [Expression]
|
||||||
} 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 {
|
||||||
memIndex :: MemoryIndex,
|
dataMode :: DataMode,
|
||||||
offset :: Expression,
|
|
||||||
chunk :: LBS.ByteString
|
chunk :: LBS.ByteString
|
||||||
} deriving (Show, Eq, Generic, NFData)
|
} deriving (Show, Eq, Generic, NFData)
|
||||||
|
|
||||||
|
|||||||
+305
-151
@@ -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, maybeToList, catMaybes)
|
import Data.Maybe (fromMaybe, catMaybes)
|
||||||
import Numeric.Natural (Natural)
|
import Numeric.Natural (Natural)
|
||||||
import Prelude hiding ((<>))
|
import Prelude hiding ((<>))
|
||||||
|
|
||||||
import Control.Monad (foldM)
|
import Control.Monad (foldM, forM_, when, unless)
|
||||||
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,20 +32,23 @@ data ValidationError =
|
|||||||
| MemoryLimitExceeded
|
| MemoryLimitExceeded
|
||||||
| AlignmentOverflow
|
| AlignmentOverflow
|
||||||
| MoreThanOneMemory
|
| MoreThanOneMemory
|
||||||
| MoreThanOneTable
|
| FunctionIndexOutOfRange Natural
|
||||||
| FunctionIndexOutOfRange
|
|
||||||
| TableIndexOutOfRange Natural
|
| TableIndexOutOfRange Natural
|
||||||
| MemoryIndexOutOfRange Natural
|
| MemoryIndexOutOfRange Natural
|
||||||
| LocalIndexOutOfRange Natural
|
| LocalIndexOutOfRange Natural
|
||||||
| GlobalIndexOutOfRange Natural
|
| GlobalIndexOutOfRange Natural
|
||||||
|
| ElemIndexOutOfRange Natural
|
||||||
|
| DataIndexOutOfRange Natural
|
||||||
| LabelIndexOutOfRange
|
| LabelIndexOutOfRange
|
||||||
| 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 ()
|
||||||
@@ -62,13 +65,14 @@ instance Monoid ValidationResult where
|
|||||||
|
|
||||||
isValid :: ValidationResult -> Bool
|
isValid :: ValidationResult -> Bool
|
||||||
isValid (Right ()) = True
|
isValid (Right ()) = True
|
||||||
isValid (Left reason) = Debug.trace ("Module mismatched with reason " ++ show reason) $ False
|
isValid (Left 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)
|
||||||
|
|
||||||
@@ -103,6 +107,11 @@ 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
|
||||||
@@ -119,6 +128,12 @@ 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
|
||||||
@@ -127,12 +142,15 @@ data Ctx = Ctx {
|
|||||||
types :: [FuncType],
|
types :: [FuncType],
|
||||||
funcs :: [FuncType],
|
funcs :: [FuncType],
|
||||||
tables :: [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)
|
||||||
@@ -197,24 +215,28 @@ getResultType (TypeIndex typeIdx) = do
|
|||||||
Ctx { types } <- ask
|
Ctx { types } <- ask
|
||||||
maybeToEither TypeIndexOutOfRange $ results <$> types !? typeIdx
|
maybeToEither TypeIndexOutOfRange $ results <$> types !? typeIdx
|
||||||
|
|
||||||
getInstrType :: Instruction Natural -> Checker Arrow
|
elemTypeToRefType :: ElemType -> ValueType
|
||||||
getInstrType Unreachable = return $ Any ==> Any
|
elemTypeToRefType FuncRef = Func
|
||||||
getInstrType Nop = return $ empty ==> empty
|
elemTypeToRefType ExternRef = Extern
|
||||||
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
|
||||||
@@ -227,184 +249,271 @@ 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 (BrTable lbls lbl) = do
|
getInstrType stack (BrTable lbls lbl) = do
|
||||||
r <- getLabel lbl
|
r <- getLabel lbl
|
||||||
rs <- mapM getLabel lbls
|
let returns lbl = do
|
||||||
if all (== r) rs
|
args <- map Val <$> getLabel lbl
|
||||||
|
res <- matchStack stack (Val I32 : reverse args) []
|
||||||
|
return (args, res)
|
||||||
|
alternatives <- mapM returns lbls
|
||||||
|
(_, def) <- returns lbl
|
||||||
|
if all (\(args, res) -> res == def && length args == length r) alternatives
|
||||||
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 $ asArrow <$> funcs !? fun
|
maybeToEither (FunctionIndexOutOfRange fun) $ asArrow <$> funcs !? fun
|
||||||
getInstrType (CallIndirect sign) = do
|
getInstrType _ (CallIndirect tableIdx sign) = do
|
||||||
Ctx { types, tables } <- ask
|
Ctx { types, tables } <- ask
|
||||||
if length tables < 1
|
if length tables <= fromIntegral tableIdx
|
||||||
then throwError (TableIndexOutOfRange 0)
|
then throwError (TableIndexOutOfRange tableIdx)
|
||||||
else do
|
else do
|
||||||
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 = do
|
getInstrType _ (Select Nothing) = do
|
||||||
var <- freshVar
|
var <- return NonRefVar
|
||||||
return $ [var, var, Val I32] ==> var
|
return $ [var, var, Val I32] ==> var
|
||||||
getInstrType (GetLocal local) = do
|
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
|
||||||
|
return $ var ==> Val I32
|
||||||
|
getInstrType _ (RefFunc funIdx) = do
|
||||||
|
Ctx { funcs, refs } <- ask
|
||||||
|
if fromIntegral funIdx < length funcs
|
||||||
|
then do
|
||||||
|
unless (Set.member funIdx refs) $
|
||||||
|
throwError $ UndeclaredFunctionRef $ fromIntegral funIdx
|
||||||
|
return $ empty ==> Val Func
|
||||||
|
else throwError $ FunctionIndexOutOfRange $ fromIntegral funIdx
|
||||||
|
getInstrType _ (GetLocal local) = do
|
||||||
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 (I32Load8S memarg) = do
|
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 (I32Store8 memarg) = do
|
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 CurrentMemory = do
|
getInstrType _ MemorySize = do
|
||||||
Ctx { mems } <- ask
|
Ctx { mems } <- ask
|
||||||
if length mems < 1 then throwError (MemoryIndexOutOfRange 0) else return $ empty ==> I32
|
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||||
getInstrType GrowMemory = do
|
return $ empty ==> I32
|
||||||
|
getInstrType _ MemoryGrow = do
|
||||||
Ctx { mems } <- ask
|
Ctx { mems } <- ask
|
||||||
if length mems < 1 then throwError (MemoryIndexOutOfRange 0) else return $ I32 ==> I32
|
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||||
getInstrType (I32Const _) = return $ empty ==> I32
|
return $ I32 ==> I32
|
||||||
getInstrType (I64Const _) = return $ empty ==> I64
|
getInstrType _ MemoryFill = do
|
||||||
getInstrType (F32Const _) = return $ empty ==> F32
|
Ctx { mems } <- ask
|
||||||
getInstrType (F64Const _) = return $ empty ==> F64
|
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||||
getInstrType (IUnOp BS32 _) = return $ I32 ==> I32
|
return $ [I32, I32, I32] ==> empty
|
||||||
getInstrType (IUnOp BS64 _) = return $ I64 ==> I64
|
getInstrType _ MemoryCopy = do
|
||||||
getInstrType (IBinOp BS32 _) = return $ [I32, I32] ==> I32
|
Ctx { mems } <- ask
|
||||||
getInstrType (IBinOp BS64 _) = return $ [I64, I64] ==> I64
|
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||||
getInstrType I32Eqz = return $ I32 ==> I32
|
return $ [I32, I32, I32] ==> empty
|
||||||
getInstrType I64Eqz = return $ I64 ==> I32
|
getInstrType _ (MemoryInit dataIdx) = do
|
||||||
getInstrType (IRelOp BS32 _) = return $ [I32, I32] ==> I32
|
Ctx { mems, datas } <- ask
|
||||||
getInstrType (IRelOp BS64 _) = return $ [I64, I64] ==> I32
|
when (length mems < 1) $ throwError (MemoryIndexOutOfRange 0)
|
||||||
getInstrType (FUnOp BS32 _) = return $ F32 ==> F32
|
when (length datas <= fromIntegral dataIdx) $ throwError (DataIndexOutOfRange dataIdx)
|
||||||
getInstrType (FUnOp BS64 _) = return $ F64 ==> F64
|
return $ [I32, I32, I32] ==> empty
|
||||||
getInstrType (FBinOp BS32 _) = return $ [F32, F32] ==> F32
|
getInstrType _ (DataDrop dataIdx) = do
|
||||||
getInstrType (FBinOp BS64 _) = return $ [F64, F64] ==> F64
|
Ctx { datas } <- ask
|
||||||
getInstrType (FRelOp BS32 _) = return $ [F32, F32] ==> I32
|
when (length datas <= fromIntegral dataIdx) $ throwError (DataIndexOutOfRange dataIdx)
|
||||||
getInstrType (FRelOp BS64 _) = return $ [F64, F64] ==> I32
|
return $ empty ==> empty
|
||||||
getInstrType I32WrapI64 = return $ I64 ==> I32
|
getInstrType _ (TableInit tableIdx elemIdx) = do
|
||||||
getInstrType (ITruncFU BS32 BS32) = return $ F32 ==> I32
|
Ctx { tables, elems } <- ask
|
||||||
getInstrType (ITruncFU BS32 BS64) = return $ F64 ==> I32
|
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||||
getInstrType (ITruncFU BS64 BS32) = return $ F32 ==> I64
|
when (length elems <= fromIntegral elemIdx) $ throwError (ElemIndexOutOfRange elemIdx)
|
||||||
getInstrType (ITruncFU BS64 BS64) = return $ F64 ==> I64
|
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||||
getInstrType (ITruncFS BS32 BS32) = return $ F32 ==> I32
|
let elemType = elems !! fromIntegral elemIdx
|
||||||
getInstrType (ITruncFS BS32 BS64) = return $ F64 ==> I32
|
when (elemType /= tableType) $ throwError (RefTypeMismatch tableType elemType)
|
||||||
getInstrType (ITruncFS BS64 BS32) = return $ F32 ==> I64
|
return $ [I32, I32, I32] ==> empty
|
||||||
getInstrType (ITruncFS BS64 BS64) = return $ F64 ==> I64
|
getInstrType _ (TableCopy toIdx fromIdx) = do
|
||||||
getInstrType (ITruncSatFU BS32 BS32) = return $ F32 ==> I32
|
Ctx { tables } <- ask
|
||||||
getInstrType (ITruncSatFU BS32 BS64) = return $ F64 ==> I32
|
let (from, to) = (fromIntegral fromIdx, fromIntegral toIdx)
|
||||||
getInstrType (ITruncSatFU BS64 BS32) = return $ F32 ==> I64
|
when (length tables <= from) $ throwError (TableIndexOutOfRange fromIdx)
|
||||||
getInstrType (ITruncSatFU BS64 BS64) = return $ F64 ==> I64
|
when (length tables <= to) $ throwError (TableIndexOutOfRange toIdx)
|
||||||
getInstrType (ITruncSatFS BS32 BS32) = return $ F32 ==> I32
|
let TableType _ fromType = tables !! from
|
||||||
getInstrType (ITruncSatFS BS32 BS64) = return $ F64 ==> I32
|
let TableType _ toType = tables !! to
|
||||||
getInstrType (ITruncSatFS BS64 BS32) = return $ F32 ==> I64
|
when (fromType /= toType) $ throwError (RefTypeMismatch fromType toType)
|
||||||
getInstrType (ITruncSatFS BS64 BS64) = return $ F64 ==> I64
|
return $ [I32, I32, I32] ==> empty
|
||||||
getInstrType I64ExtendSI32 = return $ I32 ==> I64
|
getInstrType _ (TableFill tableIdx) = do
|
||||||
getInstrType I64ExtendUI32 = return $ I32 ==> I64
|
Ctx { tables } <- ask
|
||||||
getInstrType (FConvertIU BS32 BS32) = return $ I32 ==> F32
|
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||||
getInstrType (FConvertIU BS32 BS64) = return $ I64 ==> F32
|
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||||
getInstrType (FConvertIU BS64 BS32) = return $ I32 ==> F64
|
return $ [I32, elemTypeToRefType tableType, I32] ==> empty
|
||||||
getInstrType (FConvertIU BS64 BS64) = return $ I64 ==> F64
|
getInstrType _ (TableSize tableIdx) = do
|
||||||
getInstrType (FConvertIS BS32 BS32) = return $ I32 ==> F32
|
Ctx { tables } <- ask
|
||||||
getInstrType (FConvertIS BS32 BS64) = return $ I64 ==> F32
|
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||||
getInstrType (FConvertIS BS64 BS32) = return $ I32 ==> F64
|
return $ empty ==> I32
|
||||||
getInstrType (FConvertIS BS64 BS64) = return $ I64 ==> F64
|
getInstrType _ (TableGrow tableIdx) = do
|
||||||
getInstrType F32DemoteF64 = return $ F64 ==> F32
|
Ctx { tables } <- ask
|
||||||
getInstrType F64PromoteF32 = return $ F32 ==> F64
|
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||||
getInstrType (IReinterpretF BS32) = return $ F32 ==> I32
|
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||||
getInstrType (IReinterpretF BS64) = return $ F64 ==> I64
|
return $ [elemTypeToRefType tableType, I32] ==> I32
|
||||||
getInstrType (FReinterpretI BS32) = return $ I32 ==> F32
|
getInstrType _ (TableGet tableIdx) = do
|
||||||
getInstrType (FReinterpretI BS64) = return $ I64 ==> F64
|
Ctx { tables } <- ask
|
||||||
|
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
|
||||||
|
let TableType _ tableType = tables !! fromIntegral tableIdx
|
||||||
|
return $ I32 ==> (elemTypeToRefType tableType)
|
||||||
|
getInstrType _ (TableSet tableIdx) = do
|
||||||
|
Ctx { 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 _ (IUnOp BS32 _) = return $ I32 ==> I32
|
||||||
|
getInstrType _ (IUnOp BS64 _) = return $ I64 ==> I64
|
||||||
|
getInstrType _ (IBinOp BS32 _) = return $ [I32, I32] ==> I32
|
||||||
|
getInstrType _ (IBinOp BS64 _) = return $ [I64, I64] ==> I64
|
||||||
|
getInstrType _ I32Eqz = return $ I32 ==> I32
|
||||||
|
getInstrType _ I64Eqz = return $ I64 ==> I32
|
||||||
|
getInstrType _ (IRelOp BS32 _) = return $ [I32, I32] ==> I32
|
||||||
|
getInstrType _ (IRelOp BS64 _) = return $ [I64, I64] ==> I32
|
||||||
|
getInstrType _ (FUnOp BS32 _) = return $ F32 ==> F32
|
||||||
|
getInstrType _ (FUnOp BS64 _) = return $ F64 ==> F64
|
||||||
|
getInstrType _ (FBinOp BS32 _) = return $ [F32, F32] ==> F32
|
||||||
|
getInstrType _ (FBinOp BS64 _) = return $ [F64, F64] ==> F64
|
||||||
|
getInstrType _ (FRelOp BS32 _) = return $ [F32, F32] ==> I32
|
||||||
|
getInstrType _ (FRelOp BS64 _) = return $ [F64, F64] ==> I32
|
||||||
|
getInstrType _ I32WrapI64 = return $ I64 ==> I32
|
||||||
|
getInstrType _ (ITruncFU BS32 BS32) = return $ F32 ==> I32
|
||||||
|
getInstrType _ (ITruncFU BS32 BS64) = return $ F64 ==> I32
|
||||||
|
getInstrType _ (ITruncFU BS64 BS32) = return $ F32 ==> I64
|
||||||
|
getInstrType _ (ITruncFU BS64 BS64) = return $ F64 ==> I64
|
||||||
|
getInstrType _ (ITruncFS BS32 BS32) = return $ F32 ==> I32
|
||||||
|
getInstrType _ (ITruncFS BS32 BS64) = return $ F64 ==> I32
|
||||||
|
getInstrType _ (ITruncFS BS64 BS32) = return $ F32 ==> I64
|
||||||
|
getInstrType _ (ITruncFS BS64 BS64) = return $ F64 ==> I64
|
||||||
|
getInstrType _ (ITruncSatFU BS32 BS32) = return $ F32 ==> I32
|
||||||
|
getInstrType _ (ITruncSatFU BS32 BS64) = return $ F64 ==> I32
|
||||||
|
getInstrType _ (ITruncSatFU BS64 BS32) = return $ F32 ==> I64
|
||||||
|
getInstrType _ (ITruncSatFU BS64 BS64) = return $ F64 ==> I64
|
||||||
|
getInstrType _ (ITruncSatFS BS32 BS32) = return $ F32 ==> I32
|
||||||
|
getInstrType _ (ITruncSatFS BS32 BS64) = return $ F64 ==> I32
|
||||||
|
getInstrType _ (ITruncSatFS BS64 BS32) = return $ F32 ==> I64
|
||||||
|
getInstrType _ (ITruncSatFS BS64 BS64) = return $ F64 ==> I64
|
||||||
|
getInstrType _ I64ExtendSI32 = return $ I32 ==> I64
|
||||||
|
getInstrType _ I64ExtendUI32 = return $ I32 ==> I64
|
||||||
|
getInstrType _ (FConvertIU BS32 BS32) = return $ I32 ==> F32
|
||||||
|
getInstrType _ (FConvertIU BS32 BS64) = return $ I64 ==> F32
|
||||||
|
getInstrType _ (FConvertIU BS64 BS32) = return $ I32 ==> F64
|
||||||
|
getInstrType _ (FConvertIU BS64 BS64) = return $ I64 ==> F64
|
||||||
|
getInstrType _ (FConvertIS BS32 BS32) = return $ I32 ==> F32
|
||||||
|
getInstrType _ (FConvertIS BS32 BS64) = return $ I64 ==> F32
|
||||||
|
getInstrType _ (FConvertIS BS64 BS32) = return $ I32 ==> F64
|
||||||
|
getInstrType _ (FConvertIS BS64 BS64) = return $ I64 ==> F64
|
||||||
|
getInstrType _ F32DemoteF64 = return $ F64 ==> F32
|
||||||
|
getInstrType _ F64PromoteF32 = return $ F32 ==> F64
|
||||||
|
getInstrType _ (IReinterpretF BS32) = return $ F32 ==> I32
|
||||||
|
getInstrType _ (IReinterpretF BS64) = return $ F64 ==> I64
|
||||||
|
getInstrType _ (FReinterpretI BS32) = return $ I32 ==> F32
|
||||||
|
getInstrType _ (FReinterpretI BS64) = return $ I64 ==> F64
|
||||||
|
|
||||||
|
|
||||||
replace :: (Eq a) => a -> a -> [a] -> [a]
|
replace :: (Eq a) => a -> a -> [a] -> [a]
|
||||||
@@ -416,25 +525,46 @@ 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 instr
|
(f `Arrow` t) <- getInstrType stack instr
|
||||||
matchStack stack (reverse f) t
|
matchStack stack (reverse f) t
|
||||||
|
|
||||||
matchStack :: [VType] -> [VType] -> [VType] -> Checker [VType]
|
isRef :: ValueType -> Bool
|
||||||
matchStack stack@(Any:_) _arg res = return $ res ++ stack
|
isRef (Func) = True
|
||||||
matchStack (Val v:stack) (Val v':args) res =
|
isRef (Extern) = True
|
||||||
if v == v'
|
isRef _ = False
|
||||||
then matchStack stack args res
|
|
||||||
else throwError $ TypeMismatch ((reverse $ Val v':args) `Arrow` res) ([] `Arrow` (Val v:stack))
|
matchStack :: [VType] -> [VType] -> [VType] -> Checker [VType]
|
||||||
matchStack _ (Any:_) res = return $ res
|
matchStack stack@(Any:_) _arg res = return $ res ++ stack
|
||||||
matchStack (Val v:stack) (Var:args) res =
|
matchStack (Val v:stack) (Val v':args) res =
|
||||||
let subst = replace Var (Val v) in
|
if v == v'
|
||||||
matchStack stack (subst args) (subst res)
|
then matchStack stack args res
|
||||||
matchStack (Var:stack) (Val v:args) res =
|
else throwError $ TypeMismatch ((reverse $ Val v':args) `Arrow` res) ([] `Arrow` (Val v:stack))
|
||||||
let subst = replace Var (Val v) in
|
matchStack _ (Any:_) res = return $ res
|
||||||
matchStack stack (subst args) (subst res)
|
matchStack (Val v:stack) (Var:args) res =
|
||||||
matchStack stack [] res = return $ res ++ stack
|
let subst = replace Var (Val v) in
|
||||||
matchStack [] args res = throwError $ TypeMismatch ((reverse args) `Arrow` res) ([] `Arrow` [])
|
matchStack stack (subst args) (subst res)
|
||||||
matchStack _ _ _ = error "inconsistent checker state"
|
matchStack (Var:stack) (Val v:args) res =
|
||||||
|
let subst = replace Var (Val v) in
|
||||||
|
matchStack stack (subst args) (subst res)
|
||||||
|
matchStack (Val v:stack) (NonRefVar:args) res =
|
||||||
|
let subst = replace NonRefVar (Val v) in
|
||||||
|
if isRef v
|
||||||
|
then throwError $ TypeMismatch (empty ==> empty) (empty ==> empty)
|
||||||
|
else matchStack stack (subst args) (subst res)
|
||||||
|
matchStack (NonRefVar:stack) (Val v:args) res =
|
||||||
|
let subst = replace NonRefVar (Val v) in
|
||||||
|
if isRef v
|
||||||
|
then throwError $ TypeMismatch (empty ==> empty) (empty ==> empty)
|
||||||
|
else matchStack stack (subst args) (subst res)
|
||||||
|
matchStack (Var:stack) (NonRefVar:args) res =
|
||||||
|
let subst = replace NonRefVar NonRefVar in
|
||||||
|
matchStack stack (subst args) (subst res)
|
||||||
|
matchStack (NonRefVar:stack) (Var:args) res =
|
||||||
|
let subst = replace Var NonRefVar in
|
||||||
|
matchStack stack (subst args) (subst res)
|
||||||
|
matchStack stack [] res = return $ res ++ stack
|
||||||
|
matchStack [] args res = throwError $ TypeMismatch ((reverse args) `Arrow` res) ([] `Arrow` [])
|
||||||
|
matchStack st args res = error $ "inconsistent checker state: " ++ show (st, args, res)
|
||||||
|
|
||||||
getExpressionType :: Expression -> Checker Arrow
|
getExpressionType :: Expression -> Checker Arrow
|
||||||
getExpressionType = getExpressionTypeWithInput []
|
getExpressionType = getExpressionTypeWithInput []
|
||||||
@@ -445,6 +575,8 @@ 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 ((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
|
||||||
@@ -464,7 +596,8 @@ 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@Module {types, tables, mems, globals, imports} =
|
ctxFromModule locals labels returns m =
|
||||||
|
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
|
||||||
@@ -472,12 +605,17 @@ ctxFromModule locals labels returns m@Module {types, tables, mems, globals, impo
|
|||||||
types,
|
types,
|
||||||
funcs = getFuncTypes m,
|
funcs = getFuncTypes m,
|
||||||
tables = 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
|
||||||
@@ -489,6 +627,19 @@ ctxFromModule locals labels returns m@Module {types, tables, mems, globals, impo
|
|||||||
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
|
||||||
@@ -509,10 +660,7 @@ 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
|
||||||
let res' = foldl' (\r (Table t) -> r <> isValidTableType t) res tables in
|
foldl' (\r (Table t) -> r <> isValidTableType t) res tables
|
||||||
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) _) =
|
||||||
@@ -557,24 +705,29 @@ 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 tableIdx offset funs) =
|
isElemValid ctx (ElemSegment elemType mode elements) = do
|
||||||
let check = runChecker ctx $ do
|
unless (elemType == FuncRef)
|
||||||
|
$ throwError $ RefTypeMismatch FuncRef elemType
|
||||||
|
forM_ elements $ \elem -> runChecker ctx $ do
|
||||||
|
arr <- getExpressionType elem
|
||||||
|
isConstExpression elem
|
||||||
|
unless (isValidRef elemType arr)
|
||||||
|
$ throwError $ RefTypeMismatch elemType elemType
|
||||||
|
case mode of
|
||||||
|
Active tableIdx offset -> runChecker ctx $ do
|
||||||
isConstExpression offset
|
isConstExpression offset
|
||||||
t <- getExpressionType offset
|
t <- getExpressionType offset
|
||||||
if isArrowMatch (empty ==> I32) t
|
unless (isArrowMatch (empty ==> I32) t) $ do
|
||||||
then return ()
|
throwError $ TypeMismatch t (empty ==> I32)
|
||||||
else throwError $ TypeMismatch t (empty ==> I32)
|
let tableImports = filter isTableImport imports
|
||||||
in
|
when (tableIdx >= fromIntegral (length tableImports + length tables)) $ do
|
||||||
let tableImports = filter isTableImport imports in
|
throwError $ TableIndexOutOfRange tableIdx
|
||||||
let isTableIndexValid =
|
_ -> return ()
|
||||||
if tableIdx < (fromIntegral $ length tableImports + length tables)
|
|
||||||
then return ()
|
isValidRef :: ElemType -> Arrow -> Bool
|
||||||
else Left (TableIndexOutOfRange tableIdx)
|
isValidRef FuncRef arr | arr == (empty ==> Func) = True
|
||||||
in
|
isValidRef ExternRef arr | arr == (empty ==> Extern) = True
|
||||||
let funImports = filter isFuncImport imports in
|
isValidRef _ _ = False
|
||||||
let funsLength = fromIntegral $ length functions + length funImports in
|
|
||||||
let isFunsValid = foldMap (\i -> if i < funsLength then return () else Left FunctionIndexOutOfRange) funs in
|
|
||||||
check <> isFunsValid <> isTableIndexValid
|
|
||||||
|
|
||||||
datasShouldBeValid :: Validator
|
datasShouldBeValid :: Validator
|
||||||
datasShouldBeValid m@Module { datas, mems, imports } =
|
datasShouldBeValid m@Module { datas, mems, imports } =
|
||||||
@@ -582,7 +735,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 memIdx offset _) =
|
isDataValid ctx (DataSegment (ActiveData memIdx offset) _) =
|
||||||
let check = runChecker ctx $ do
|
let check = runChecker ctx $ do
|
||||||
isConstExpression offset
|
isConstExpression offset
|
||||||
t <- getExpressionType offset
|
t <- getExpressionType offset
|
||||||
@@ -594,6 +747,7 @@ 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 ()
|
||||||
@@ -602,7 +756,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
|
else Left $ FunctionIndexOutOfRange $ fromIntegral i
|
||||||
|
|
||||||
exportsShouldBeValid :: Validator
|
exportsShouldBeValid :: Validator
|
||||||
exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals } =
|
exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals } =
|
||||||
@@ -615,7 +769,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
|
if fromIntegral funIdx < length funcImports + length functions then return () else Left (FunctionIndexOutOfRange funIdx)
|
||||||
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-16.5
|
resolver: lts-20.23
|
||||||
packages:
|
packages:
|
||||||
- '.'
|
- '.'
|
||||||
extra-deps: []
|
extra-deps: []
|
||||||
|
|||||||
+4
-4
@@ -6,7 +6,7 @@
|
|||||||
packages: []
|
packages: []
|
||||||
snapshots:
|
snapshots:
|
||||||
- completed:
|
- completed:
|
||||||
size: 531707
|
sha256: 4c972e067bae16b95961dbfdd12e07f1ee6c8fffabbfa05c3d65100b03f548b7
|
||||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/5.yaml
|
size: 650253
|
||||||
sha256: 9751e25e0af5713a53ddcfcc79564b082c71b1b357fadef0d85672a5b5ba3703
|
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/23.yaml
|
||||||
original: lts-16.5
|
original: lts-20.23
|
||||||
|
|||||||
+4
-2
@@ -16,8 +16,10 @@ import qualified Data.List as List
|
|||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
files <- filter (List.isSuffixOf ".wast") <$> Directory.listDirectory "tests/spec"
|
files <-
|
||||||
-- let files = ["const.wast"]
|
filter (not . List.isPrefixOf "simd") . filter (List.isSuffixOf ".wast")
|
||||||
|
<$> Directory.listDirectory "tests/spec"
|
||||||
|
-- let files = ["bulk.wast"]
|
||||||
scriptTestCases <- (`mapM` files) $ \file -> do
|
scriptTestCases <- (`mapM` files) $ \file -> do
|
||||||
test <- LBS.readFile ("tests/spec/" ++ file)
|
test <- LBS.readFile ("tests/spec/" ++ file)
|
||||||
return $ testCase file $ do
|
return $ testCase file $ do
|
||||||
|
|||||||
+1
-1
Submodule tests/spec updated: 9994915e0c...6241ce9e15
+13
-13
@@ -1,6 +1,6 @@
|
|||||||
cabal-version: 2.2
|
cabal-version: 2.2
|
||||||
name: wasm
|
name: wasm
|
||||||
version: 1.0.1.0
|
version: 1.1.2
|
||||||
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
|
tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.4, GHC==9.2.7
|
||||||
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
|
||||||
@@ -62,12 +62,12 @@ library
|
|||||||
, 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.3
|
, mtl >=2.2.1 && < 2.4
|
||||||
, primitive >=0.7 && < 0.8
|
, primitive >=0.7 && < 0.8
|
||||||
, text >=1.1 && < 1.3
|
, text >=1.1 && < 3
|
||||||
, transformers >=0.4 && < 0.6
|
, transformers >=0.4 && < 0.7
|
||||||
, utf8-string >=1.0 && < 1.1
|
, utf8-string >=1.0 && < 1.1
|
||||||
, vector >=0.12 && < 0.13
|
, vector >=0.12.2 && < 0.14
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
test-suite test
|
test-suite test
|
||||||
|
|||||||
Reference in New Issue
Block a user