From fdbbdcf6bd18428f2569f57fa339d381da1f9034 Mon Sep 17 00:00:00 2001 From: Mingun Date: Tue, 10 Sep 2024 23:08:04 +0500 Subject: [PATCH] Implement validation for `valid` key Fixed (4): [info] - attr_bad_valid_expr *** FAILED *** [info] [] [info] did not equal [info] [attr_bad_valid_expr.ksy: /seq/0/valid/expr: [info] error: invalid type: expected boolean, got Int1Type(true) [info] ] (SimpleMatchers.scala:34) [info] - expr_field_unknown_valid_eq_long *** FAILED *** [info] [] [info] did not equal [info] [expr_field_unknown_valid_eq_long.ksy: /seq/0/valid/eq: [info] error: unable to access 'bar' in expr_field_unknown_valid_eq_long context [info] ] (SimpleMatchers.scala:34) [info] - expr_field_unknown_valid_expr *** FAILED *** [info] [] [info] did not equal [info] [expr_field_unknown_valid_expr.ksy: /seq/0/valid/expr: [info] error: unable to access 'bar' in expr_field_unknown_valid_expr context [info] ] (SimpleMatchers.scala:34) [info] - expr_field_unknown_valid_range *** FAILED *** [info] [] [info] did not equal [info] [expr_field_unknown_valid_range.ksy: /seq/0/valid/min: [info] error: unable to access 'bar' in expr_field_unknown_valid_range context [info] [info] expr_field_unknown_valid_range.ksy: /seq/0/valid/max: [info] error: unable to access 'qux' in expr_field_unknown_valid_range context [info] ] (SimpleMatchers.scala:34) Changed (2, must correct model to distinguish between `valid` and `valid.eq`): [info] - attr_bad_valid_eq_long *** FAILED *** [info] [attr_bad_valid_eq_long.ksy: /seq/0/valid/eq: [info] error: invalid type: expected Int1Type(false), got CalcStrType [info] ] [info] did not equal [info] [attr_bad_valid_eq_long.ksy: /seq/0/valid/eq: [info] error: can't compare Int1Type(false) and CalcStrType [info] ] (SimpleMatchers.scala:34) [info] - attr_bad_valid_eq_short *** FAILED *** [info] [attr_bad_valid_eq_short.ksy: /seq/0/valid/eq: [info] error: invalid type: expected Int1Type(false), got CalcStrType [info] ] [info] did not equal [info] [attr_bad_valid_eq_short.ksy: /seq/0/valid: [info] error: can't compare Int1Type(false) and CalcStrType [info] ] (SimpleMatchers.scala:34) Changed (5, expected must be corrected): [info] - attr_bad_valid_any_of *** FAILED *** [info] [attr_bad_valid_any_of.ksy: /seq/0/valid/any-of/0: [info] error: invalid type: expected Int1Type(false), got Int1Type(true) [info] [info] attr_bad_valid_any_of.ksy: /seq/0/valid/any-of/1: [info] error: invalid type: expected Int1Type(false), got Int1Type(true) [info] [info] attr_bad_valid_any_of.ksy: /seq/0/valid/any-of/2: [info] error: invalid type: expected Int1Type(false), got CalcBooleanType [info] [info] attr_bad_valid_any_of.ksy: /seq/0/valid/any-of/3: [info] error: invalid type: expected Int1Type(false), got Int1Type(true) [info] ] [info] did not equal [info] [attr_bad_valid_any_of.ksy: /seq/0/valid/any-of/2: [info] error: can't compare Int1Type(false) and CalcBooleanType [info] ] (SimpleMatchers.scala:34) [info] - attr_bad_valid_range *** FAILED *** [info] [attr_bad_valid_range.ksy: /seq/0/valid/min: [info] error: invalid type: expected Int1Type(false), got CalcBooleanType [info] [info] attr_bad_valid_range.ksy: /seq/0/valid/max: [info] error: invalid type: expected Int1Type(false), got CalcStrType [info] ] [info] did not equal [info] [attr_bad_valid_range.ksy: /seq/0/valid/min: [info] error: can't compare Int1Type(false) and CalcBooleanType [info] [info] attr_bad_valid_range.ksy: /seq/0/valid/max: [info] error: can't compare Int1Type(false) and CalcStrType [info] ] (SimpleMatchers.scala:34) [info] - attr_bad_valid_repeat_eq_short *** FAILED *** [info] [attr_bad_valid_repeat_eq_short.ksy: /seq/0/valid/eq: [info] error: invalid type: expected IntMultiType(true,Width4,Some(LittleEndian)), got ArrayTypeInStream(CalcIntType) [info] ] [info] did not equal [info] [attr_bad_valid_repeat_eq_short.ksy: /seq/0/valid: [info] error: can't compare IntMultiType(true,Width4,Some(LittleEndian)) and ArrayTypeInStream(CalcIntType) [info] ] (SimpleMatchers.scala:34) [info] - expr_field_unknown_valid_any_of *** FAILED *** [info] [expr_field_unknown_valid_any_of.ksy: /seq/0/valid/any-of/0: [info] error: invalid type: expected Int1Type(false), got Int1Type(true) [info] [info] expr_field_unknown_valid_any_of.ksy: /seq/0/valid/any-of/1: [info] error: unable to access 'bar' in expr_field_unknown_valid_any_of context [info] [info] expr_field_unknown_valid_any_of.ksy: /seq/0/valid/any-of/2: [info] error: invalid type: expected Int1Type(false), got Int1Type(true) [info] [info] expr_field_unknown_valid_any_of.ksy: /seq/0/valid/any-of/3: [info] error: unable to access 'qux' in expr_field_unknown_valid_any_of context [info] ] [info] did not equal [info] [expr_field_unknown_valid_any_of.ksy: /seq/0/valid/any-of/1: [info] error: unable to access 'bar' in expr_field_unknown_valid_any_of context [info] [info] expr_field_unknown_valid_any_of.ksy: /seq/0/valid/any-of/3: [info] error: unable to access 'qux' in expr_field_unknown_valid_any_of context [info] ] (SimpleMatchers.scala:34) [info] - expr_field_unknown_valid_eq_short *** FAILED *** [info] [expr_field_unknown_valid_eq_short.ksy: /seq/0/valid/eq: [info] error: unable to access 'bar' in expr_field_unknown_valid_eq_short context [info] ] [info] did not equal [info] [expr_field_unknown_valid_eq_short.ksy: /seq/0/valid: [info] error: unable to access 'bar' in expr_field_unknown_valid_eq_short context [info] ] (SimpleMatchers.scala:34) --- .../struct/precompile/TypeValidator.scala | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala b/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala index d80741484..8457b6837 100644 --- a/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala +++ b/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala @@ -82,7 +82,12 @@ class TypeValidator(specs: ClassSpecs) extends PrecompileStep { val problemsDataType = validateDataType(attr.dataType, path) - List(problemsIf, problemsRepeat, problemsDataType).flatten + val problemsValid: Iterable[CompilationProblem] = attr.valid match { + case Some(valid) => validateValidClause(valid, attr.dataType, attr.path :+ "valid") + case None => None // all good + } + + List(problemsIf, problemsRepeat, problemsDataType, problemsValid).flatten } def validateParseInstance(pis: ParseInstanceSpec): Iterable[CompilationProblem] = { @@ -100,7 +105,12 @@ class TypeValidator(specs: ClassSpecs) extends PrecompileStep { case None => None // all good } - List(problemsAttr, problemsIo, problemsPos).flatten + val problemsValid: Iterable[CompilationProblem] = pis.valid match { + case Some(valid) => validateValidClause(valid, pis.dataType, pis.path :+ "valid") + case None => None // all good + } + + List(problemsAttr, problemsIo, problemsPos, problemsValid).flatten } def validateValueInstance(vis: ValueInstanceSpec): Option[CompilationProblem] = { @@ -204,6 +214,42 @@ class TypeValidator(specs: ClassSpecs) extends PrecompileStep { } } + /** + * @param valid The `valid` clause of Kaitai Struct attribute or parse instance definition + * @param attrType The type of attribute, used to check compatibility of valid expressions to that type + * @param path Path where `valid` key is defined + */ + private def validateValidClause( + valid: ValidationSpec, + attrType: DataType, + path: List[String] + ): Iterable[CompilationProblem] = { + // TODO: This is not user-face message + val expected = attrType.toString + valid match { + // TODO: Need to distinguish between `valid`, `valid.eq` and `contents` + case ValidationEq(value) => + checkAssertObject(value, attrType, expected, path, "eq") + case ValidationMin(min) => + checkAssertObject(min, attrType, expected, path, "min") + case ValidationMax(max) => + checkAssertObject(max, attrType, expected, path, "max") + case ValidationRange(min, max) => { + checkAssertObject(min, attrType, expected, path, "min") ++ + checkAssertObject(max, attrType, expected, path, "max") + } + case ValidationAnyOf(values) => { + val itemPath = path :+ "any-of" + values.zipWithIndex.flatMap { case (value, i) => + checkAssertObject(value, attrType, expected, itemPath, i.toString) + } + } + case ValidationInEnum() => List() + case ValidationExpr(checkExpr) => + checkAssert[BooleanType](checkExpr, "boolean", path, "expr") + } + } + /** * Checks that expression's type conforms to a given datatype, otherwise * throw a human-readable exception, with some pointers that would help