Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix enum and type resolution #309

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Prev Previous commit
Next Next commit
Fix enum resolution in expressions
Mingun committed Oct 4, 2024
commit 80334070372da6fe2ae09a90e5875f01e1da679d
34 changes: 34 additions & 0 deletions jvm/src/test/scala/io/kaitai/struct/ClassTypeProvider$Test.scala
Original file line number Diff line number Diff line change
@@ -638,18 +638,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find type 'one', searching from 'root'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'one', searching from 'root'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find type 'one', searching from 'root'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root'")
}
}

@@ -667,14 +671,17 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_1::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_1::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_1'")
}
}

@@ -688,18 +695,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find enum 'e' in 'root::child_2::one'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_2::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_2::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_2'")
}
}

@@ -717,14 +728,17 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_1::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_1::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_1::one'")
}
}

@@ -738,18 +752,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find enum 'e' in 'root::child_1::two::one'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_1::two::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_1::two::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_1::two'")
}
}

@@ -763,18 +781,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find enum 'e' in 'root::child_2::one'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_2::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_2::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_2::one'")
}
}

@@ -788,18 +810,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find enum 'e' in 'root::child_2::one'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_2::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_2::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_2::two'")
}
}

@@ -813,18 +839,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find enum 'e' in 'root::child_1::two::one'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_1::two::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_1::two::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_1::two::one'")
}
}

@@ -838,18 +868,22 @@ class ClassTypeProvider$Test extends AnyFunSpec {

it("doesn't resolve 'one::e'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "e")
thrown.getMessage should be("unable to find enum 'e' in 'root::child_1::two::one'")
}

it("doesn't resolve 'one::two::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(one_two, "e")
thrown.getMessage should be("unable to find type 'two' in 'root::child_1::two::one'")
}

it("doesn't resolve 'one::unknown'") {
val thrown = the[EnumNotFoundError] thrownBy resolver.resolveEnum(one, "unknown")
thrown.getMessage should be("unable to find enum 'unknown' in 'root::child_1::two::one'")
}

it("doesn't resolve 'unknown::e'") {
val thrown = the[TypeNotFoundError] thrownBy resolver.resolveEnum(unknown, "e")
thrown.getMessage should be("unable to find type 'unknown', searching from 'root::child_1::two::two'")
}
}
}
21 changes: 17 additions & 4 deletions shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import io.kaitai.struct.datatype.DataType
import io.kaitai.struct.datatype.DataType._
import io.kaitai.struct.exprlang.Ast
import io.kaitai.struct.format._
import io.kaitai.struct.precompile.{EnumNotFoundError, FieldNotFoundError, TypeNotFoundInHierarchyError, TypeNotFoundInTypeError, TypeUndecidedError}
import io.kaitai.struct.precompile.{EnumNotFoundInHierarchyError, EnumNotFoundInTypeError, FieldNotFoundError, TypeNotFoundInHierarchyError, TypeNotFoundInTypeError, TypeUndecidedError}
import io.kaitai.struct.translators.TypeProvider

class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends TypeProvider {
@@ -90,8 +90,21 @@ class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends
throw new FieldNotFoundError(attrName, inClass)
}

override def resolveEnum(inType: Ast.typeId, enumName: String): EnumSpec =
resolveEnumName(resolveClassSpec(inType), enumName)
override def resolveEnum(inType: Ast.typeId, enumName: String): EnumSpec = {
val inClass = if (inType.absolute) topClass else nowClass
// When concrete type is not defined, search enum definition in all enclosing types
if (inType.names.isEmpty) {
resolveEnumName(inClass, enumName)
} else {
val ty = resolveTypePath(inClass, inType.names)
ty.enums.get(enumName) match {
case Some(spec) =>
spec
case None =>
throw new EnumNotFoundInTypeError(enumName, ty)
}
}
}

private def resolveEnumName(inClass: ClassSpec, enumName: String): EnumSpec = {
inClass.enums.get(enumName) match {
@@ -102,7 +115,7 @@ class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends
inClass.upClass match {
case Some(upClass) => resolveEnumName(upClass, enumName)
case None =>
throw new EnumNotFoundError(enumName, nowClass)
throw new EnumNotFoundInHierarchyError(enumName, nowClass)
}
}
}
Original file line number Diff line number Diff line change
@@ -22,8 +22,13 @@ class TypeNotFoundInTypeError(val name: String, val curClass: ClassSpec)
extends TypeNotFoundError(s"unable to find type '$name' in '${curClass.nameAsStr}'")
class FieldNotFoundError(val name: String, val curClass: ClassSpec)
extends NotFoundError(s"unable to access '$name' in '${curClass.nameAsStr}' context")
class EnumNotFoundError(val name: String, val curClass: ClassSpec)
extends NotFoundError(s"unable to find enum '$name', searching from '${curClass.nameAsStr}'")

sealed abstract class EnumNotFoundError(msg: String) extends NotFoundError(msg)
class EnumNotFoundInHierarchyError(val name: String, val curClass: ClassSpec)
extends EnumNotFoundError(s"unable to find enum '$name', searching from '${curClass.nameAsStr}'")
class EnumNotFoundInTypeError(val name: String, val curClass: ClassSpec)
extends EnumNotFoundError(s"unable to find enum '$name' in '${curClass.nameAsStr}'")

class EnumMemberNotFoundError(val label: String, val enumName: String, val enumDefPath: String)
extends NotFoundError(s"unable to find enum member '$enumName::$label' (enum '$enumName' defined at /$enumDefPath)")