add more validators
This commit is contained in:
+130
-19
@@ -25,6 +25,7 @@ import Debug.Trace as Debug
|
|||||||
data ValidationResult =
|
data ValidationResult =
|
||||||
DuplicatedExportNames [String]
|
DuplicatedExportNames [String]
|
||||||
| InvalidTableType
|
| InvalidTableType
|
||||||
|
| InvalidMemoryLimit
|
||||||
| MoreThanOneMemory
|
| MoreThanOneMemory
|
||||||
| MoreThanOneTable
|
| MoreThanOneTable
|
||||||
| IndexOutOfRange
|
| IndexOutOfRange
|
||||||
@@ -32,6 +33,8 @@ data ValidationResult =
|
|||||||
| NoTableInModule
|
| NoTableInModule
|
||||||
| NoMemoryInModule
|
| NoMemoryInModule
|
||||||
| TypeMismatch
|
| TypeMismatch
|
||||||
|
| InvalidConstantExpr
|
||||||
|
| InvalidStartFunctionType
|
||||||
| Valid
|
| Valid
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
@@ -354,7 +357,8 @@ replace _ _ [] = []
|
|||||||
replace x y (v:r) = (if x == v then y else v) : replace x y r
|
replace x y (v:r) = (if x == v then y else v) : replace x y r
|
||||||
|
|
||||||
unify :: Arrow -> Arrow -> Checker Arrow
|
unify :: Arrow -> Arrow -> Checker Arrow
|
||||||
unify (f `Arrow` t) (f' `Arrow` t') = unify' (f `Arrow` reverse t) (reverse f' `Arrow` t')
|
unify (from `Arrow` to) (from' `Arrow` to') =
|
||||||
|
unify' (from `Arrow` reverse to) (reverse from' `Arrow` to')
|
||||||
where
|
where
|
||||||
unify' :: Arrow -> Arrow -> Checker Arrow
|
unify' :: Arrow -> Arrow -> Checker Arrow
|
||||||
unify' (f `Arrow` []) (f' `Arrow` t') =
|
unify' (f `Arrow` []) (f' `Arrow` t') =
|
||||||
@@ -394,15 +398,35 @@ getExpressionType instrs =
|
|||||||
arr' <- unify a arr
|
arr' <- unify a arr
|
||||||
go arr' rest
|
go arr' rest
|
||||||
|
|
||||||
ctxFromModule :: [ValueType] -> [Maybe ValueType] -> Maybe ValueType -> Module -> Ctx
|
isConstExpression :: [Instruction] -> Checker ()
|
||||||
ctxFromModule locals labels returns Module {types, functions, tables, mems, globals, imports} =
|
isConstExpression [] = return ()
|
||||||
|
isConstExpression ((I32Const _):rest) = isConstExpression rest
|
||||||
|
isConstExpression ((I64Const _):rest) = isConstExpression rest
|
||||||
|
isConstExpression ((F32Const _):rest) = isConstExpression rest
|
||||||
|
isConstExpression ((F64Const _):rest) = isConstExpression rest
|
||||||
|
isConstExpression ((GetGlobal idx):rest) = do
|
||||||
|
Ctx {globals} <- ask
|
||||||
|
case globals !! fromIntegral idx of
|
||||||
|
Const _ -> isConstExpression rest
|
||||||
|
Mut _ -> throwError InvalidConstantExpr
|
||||||
|
isConstExpression _ = throwError InvalidConstantExpr
|
||||||
|
|
||||||
|
getFuncTypes :: Module -> [FuncType]
|
||||||
|
getFuncTypes Module {types, functions, imports} =
|
||||||
let funImports = catMaybes $ map getFuncType imports in
|
let funImports = catMaybes $ map getFuncType imports in
|
||||||
|
funImports ++ map ((types !!) . fromIntegral . funcType) functions
|
||||||
|
where
|
||||||
|
getFuncType (Import _ _ (ImportFunc typeIdx)) = Just $ types !! (fromIntegral typeIdx)
|
||||||
|
getFuncType _ = Nothing
|
||||||
|
|
||||||
|
ctxFromModule :: [ValueType] -> [Maybe ValueType] -> Maybe ValueType -> Module -> Ctx
|
||||||
|
ctxFromModule locals labels returns m@Module {types, functions, tables, mems, globals, imports} =
|
||||||
let tableImports = catMaybes $ map getTableType imports in
|
let tableImports = catMaybes $ map getTableType imports in
|
||||||
let memsImports = catMaybes $ map getMemType imports in
|
let memsImports = catMaybes $ map getMemType imports in
|
||||||
let globalImports = catMaybes $ map getGlobalType imports in
|
let globalImports = catMaybes $ map getGlobalType imports in
|
||||||
Ctx {
|
Ctx {
|
||||||
types,
|
types,
|
||||||
funcs = funImports ++ map ((types !!) . fromIntegral . funcType) functions,
|
funcs = getFuncTypes m,
|
||||||
tables = tableImports ++ map (\(Table t) -> t) tables,
|
tables = tableImports ++ map (\(Table t) -> t) tables,
|
||||||
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,
|
||||||
@@ -411,9 +435,6 @@ ctxFromModule locals labels returns Module {types, functions, tables, mems, glob
|
|||||||
returns
|
returns
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
getFuncType (Import _ _ (ImportFunc typeIdx)) = Just $ types !! (fromIntegral typeIdx)
|
|
||||||
getFuncType _ = Nothing
|
|
||||||
|
|
||||||
getTableType (Import _ _ (ImportTable tableType)) = Just tableType
|
getTableType (Import _ _ (ImportTable tableType)) = Just tableType
|
||||||
getTableType _ = Nothing
|
getTableType _ = Nothing
|
||||||
|
|
||||||
@@ -449,20 +470,106 @@ tablesShouldBeValid Module { imports, tables } =
|
|||||||
else MoreThanOneTable
|
else MoreThanOneTable
|
||||||
where
|
where
|
||||||
isValidTableType :: TableType -> ValidationResult
|
isValidTableType :: TableType -> ValidationResult
|
||||||
isValidTableType (TableType (Limit min max) _) = if min <= fromMaybe min max then Valid else InvalidTableType
|
isValidTableType (TableType (Limit min max) _) =
|
||||||
|
if min <= fromMaybe min max
|
||||||
|
then Valid
|
||||||
|
else InvalidTableType
|
||||||
|
|
||||||
isTableImport Import { desc = ImportTable _ } = True
|
isTableImport :: Import -> Bool
|
||||||
isTableImport _ = False
|
isTableImport Import { desc = ImportTable _ } = True
|
||||||
|
isTableImport _ = False
|
||||||
|
|
||||||
shouldBeAtMostOneMemory :: Validator
|
memoryShouldBeValid :: Validator
|
||||||
shouldBeAtMostOneMemory Module { imports, mems } =
|
memoryShouldBeValid Module { imports, mems } =
|
||||||
let memImports = filter isMemImport imports in
|
let memImports = filter isMemImport imports in
|
||||||
|
let res = foldMap (\Import { desc = ImportMemory l } -> isValidLimit l) memImports in
|
||||||
|
let res' = foldl' (\r (Memory l) -> r <> isValidLimit l) res mems in
|
||||||
if length memImports + length mems <= 1
|
if length memImports + length mems <= 1
|
||||||
then Valid
|
then res'
|
||||||
else MoreThanOneMemory
|
else MoreThanOneMemory
|
||||||
where
|
where
|
||||||
isMemImport Import { desc = ImportMemory _ } = True
|
isValidLimit :: Limit -> ValidationResult
|
||||||
isMemImport _ = False
|
isValidLimit (Limit min max) = if min <= fromMaybe min max then Valid else InvalidMemoryLimit
|
||||||
|
|
||||||
|
isMemImport :: Import -> Bool
|
||||||
|
isMemImport Import { desc = ImportMemory _ } = True
|
||||||
|
isMemImport _ = False
|
||||||
|
|
||||||
|
globalsShouldBeValid :: Validator
|
||||||
|
globalsShouldBeValid m@Module { imports, globals } =
|
||||||
|
let ctx = ctxFromModule [] [] Nothing m in
|
||||||
|
foldMap (isGlobalValid ctx) globals
|
||||||
|
where
|
||||||
|
getGlobalType :: GlobalType -> ValueType
|
||||||
|
getGlobalType (Const vt) = vt
|
||||||
|
getGlobalType (Mut vt) = vt
|
||||||
|
|
||||||
|
isGlobalValid :: Ctx -> Global -> ValidationResult
|
||||||
|
isGlobalValid ctx (Global gt init) =
|
||||||
|
let check = runChecker ctx $ do
|
||||||
|
isConstExpression init
|
||||||
|
t <- getExpressionType init
|
||||||
|
return $ isArrowMatch (empty ==> getGlobalType gt) t
|
||||||
|
in
|
||||||
|
case check of
|
||||||
|
Left err -> err
|
||||||
|
Right eq -> if eq then Valid else TypeMismatch
|
||||||
|
|
||||||
|
elemsShouldBeValid :: Validator
|
||||||
|
elemsShouldBeValid m@Module { elems, functions, tables, imports } =
|
||||||
|
let ctx = ctxFromModule [] [] Nothing m in
|
||||||
|
foldMap (isElemValid ctx) elems
|
||||||
|
where
|
||||||
|
isElemValid :: Ctx -> ElemSegment -> ValidationResult
|
||||||
|
isElemValid ctx (ElemSegment tableIdx offset funs) =
|
||||||
|
let check = runChecker ctx $ do
|
||||||
|
isConstExpression offset
|
||||||
|
t <- getExpressionType offset
|
||||||
|
return $ isArrowMatch (empty ==> I32) t
|
||||||
|
in
|
||||||
|
let isIniterValid = case check of
|
||||||
|
Left err -> err
|
||||||
|
Right eq -> if eq then Valid else TypeMismatch
|
||||||
|
in
|
||||||
|
let tableImports = filter isTableImport imports in
|
||||||
|
let isTableIndexValid =
|
||||||
|
if tableIdx < (fromIntegral $ length tableImports + length tables)
|
||||||
|
then Valid
|
||||||
|
else IndexOutOfRange
|
||||||
|
in
|
||||||
|
let funsLength = fromIntegral $ length functions in
|
||||||
|
let isFunsValid = foldMap (\i -> if i < funsLength then Valid else IndexOutOfRange) funs in
|
||||||
|
isIniterValid <> isFunsValid <> isTableIndexValid
|
||||||
|
|
||||||
|
datasShouldBeValid :: Validator
|
||||||
|
datasShouldBeValid m@Module { datas, mems, imports } =
|
||||||
|
let ctx = ctxFromModule [] [] Nothing m in
|
||||||
|
foldMap (isDataValid ctx) datas
|
||||||
|
where
|
||||||
|
isDataValid :: Ctx -> DataSegment -> ValidationResult
|
||||||
|
isDataValid ctx (DataSegment memIdx offset _) =
|
||||||
|
let check = runChecker ctx $ do
|
||||||
|
isConstExpression offset
|
||||||
|
t <- getExpressionType offset
|
||||||
|
return $ isArrowMatch (empty ==> I32) t
|
||||||
|
in
|
||||||
|
let isOffsetValid = case check of
|
||||||
|
Left err -> err
|
||||||
|
Right eq -> if eq then Valid else TypeMismatch
|
||||||
|
in
|
||||||
|
let memImports = filter isMemImport imports in
|
||||||
|
if memIdx < (fromIntegral $ length memImports + length mems)
|
||||||
|
then isOffsetValid
|
||||||
|
else IndexOutOfRange
|
||||||
|
|
||||||
|
startShouldBeValid :: Validator
|
||||||
|
startShouldBeValid Module { start = Nothing } = Valid
|
||||||
|
startShouldBeValid m@Module { start = Just (StartFunction idx) } =
|
||||||
|
let types = getFuncTypes m in
|
||||||
|
let i = fromIntegral idx in
|
||||||
|
if length types > i
|
||||||
|
then if FuncType [] [] == types !! i then Valid else InvalidStartFunctionType
|
||||||
|
else IndexOutOfRange
|
||||||
|
|
||||||
exportNamesShouldBeDifferent :: Validator
|
exportNamesShouldBeDifferent :: Validator
|
||||||
exportNamesShouldBeDifferent Module { exports } =
|
exportNamesShouldBeDifferent Module { exports } =
|
||||||
@@ -481,8 +588,12 @@ validate mod = foldMap ($ mod) validators
|
|||||||
where
|
where
|
||||||
validators :: [Validator]
|
validators :: [Validator]
|
||||||
validators = [
|
validators = [
|
||||||
|
functionsShouldBeValid,
|
||||||
tablesShouldBeValid,
|
tablesShouldBeValid,
|
||||||
shouldBeAtMostOneMemory,
|
memoryShouldBeValid,
|
||||||
exportNamesShouldBeDifferent,
|
globalsShouldBeValid,
|
||||||
functionsShouldBeValid
|
elemsShouldBeValid,
|
||||||
|
datasShouldBeValid,
|
||||||
|
startShouldBeValid,
|
||||||
|
exportNamesShouldBeDifferent
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user