Skip to content

Commit 2c0967d

Browse files
committed
Fix leaking of types and enums from other imported specs to specs which does not import them explicitly
1 parent 420d877 commit 2c0967d

File tree

4 files changed

+37
-8
lines changed

4 files changed

+37
-8
lines changed

shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,12 @@ class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends
155155
case Some(upClass) => resolveTypeName(upClass, typeName)
156156
case None =>
157157
classSpecs.get(typeName) match {
158-
case Some(spec) => spec
159-
case None =>
158+
// We should use that spec if it is imported in our file (which is represented
159+
// by our top-level class). If `topClass` imports `classSpec`, we could try to
160+
// resolve type in it
161+
// TODO: if type is defined in spec, we could add a suggestion to error to add missing import
162+
case Some(spec) if (topClass.imports.contains(spec)) => spec
163+
case _ =>
160164
throw new TypeNotFoundInHierarchyError(typeName, nowClass)
161165
}
162166
}

shared/src/main/scala/io/kaitai/struct/format/ClassSpec.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ case class ClassSpec(
8181

8282
var seqSize: Sized = NotCalculatedSized
8383

84+
/**
85+
* The list of top-level type specifications which is imported to the file,
86+
* which top-level type is represented by this class.
87+
*
88+
* This collection filled only for top-level classes (for which [[upClass]] is `None`).
89+
*
90+
* This collection is filled by the [[io.kaitai.struct.precompile.LoadImports]] pass.
91+
*/
92+
var imports = mutable.ListBuffer[ClassSpec]()
93+
8494
def toDataType: DataType = {
8595
val cut = CalcUserType(name, None)
8696
cut.classSpec = Some(this)

shared/src/main/scala/io/kaitai/struct/precompile/LoadImports.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class LoadImports(specs: ClassSpecs) {
3232
loadImport(
3333
name,
3434
curClass.meta.path ++ List("imports", idx.toString),
35-
Some(curClass.fileNameAsStr),
35+
curClass,
3636
workDir
3737
)
3838
}).map((x) => x.flatten)
@@ -44,9 +44,10 @@ class LoadImports(specs: ClassSpecs) {
4444
Future.sequence(List(thisMetaFuture, nestedFuture)).map((x) => x.flatten)
4545
}
4646

47-
private def loadImport(name: String, path: List[String], inFile: Option[String], workDir: ImportPath): Future[List[ClassSpec]] = {
47+
private def loadImport(name: String, path: List[String], curClass: ClassSpec, workDir: ImportPath): Future[List[ClassSpec]] = {
4848
Log.importOps.info(() => s".. LoadImports: loadImport($name, workDir = $workDir)")
4949

50+
val inFile = Some(curClass.fileNameAsStr)
5051
val impPath = ImportPath.fromString(name)
5152
val fullPath = ImportPath.add(workDir, impPath)
5253

@@ -63,8 +64,9 @@ class LoadImports(specs: ClassSpecs) {
6364
s".. LoadImports: loadImport($name, workDir = $workDir), got spec=$specNameAsStr"
6465
})
6566
optSpec match {
66-
case Some(spec) =>
67-
val specName = spec.name.head
67+
case Some(importedSpec) =>
68+
curClass.imports += importedSpec
69+
val specName = importedSpec.name.head
6870
// Check if spec name does not match file name. If it doesn't match,
6971
// it is probably already a serious error.
7072
if (name != specName)
@@ -88,12 +90,12 @@ class LoadImports(specs: ClassSpecs) {
8890
val isNewSpec = specs.synchronized {
8991
val isNew = !specs.contains(specName)
9092
if (isNew) {
91-
specs(specName) = spec
93+
specs(specName) = importedSpec
9294
}
9395
isNew
9496
}
9597
if (isNewSpec) {
96-
processClass(spec, ImportPath.updateWorkDir(workDir, impPath))
98+
processClass(importedSpec, ImportPath.updateWorkDir(workDir, impPath))
9799
} else {
98100
Log.importOps.warn(() => s"... we have that already, ignoring")
99101
Future { List() }

shared/src/main/scala/io/kaitai/struct/precompile/ResolveTypes.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import io.kaitai.struct.problems._
1010
/**
1111
* A collection of methods that resolves user types and enum types, i.e.
1212
* converts names into ClassSpec / EnumSpec references.
13+
*
14+
* This step runs for each top-level [[format.ClassSpec]].
1315
*/
1416
class ResolveTypes(specs: ClassSpecs, topClass: ClassSpec, opaqueTypes: Boolean) extends PrecompileStep {
17+
/** Resolves references to types and enums in `topClass` and all its nested types. */
1518
override def run(): Iterable[CompilationProblem] =
1619
topClass.mapRec(resolveUserTypes).map(problem => problem.localizedInType(topClass))
1720

@@ -44,6 +47,16 @@ class ResolveTypes(specs: ClassSpecs, topClass: ClassSpec, opaqueTypes: Boolean)
4447
private def resolveUserTypeForMember(curClass: ClassSpec, attr: MemberSpec): Iterable[CompilationProblem] =
4548
resolveUserType(curClass, attr.dataType, attr.path)
4649

50+
/**
51+
* Resolves any references to typee or enum in `dataType` used in `curClass` to
52+
* a type definition, or returns [[TypeNotFoundErr]] or [[EnumNotFoundErr]] error.
53+
*
54+
* @param curClass Class that contains member
55+
* @param dataType Definition of an attribute type which references to a type or enum need to be resolved
56+
* @param path A path to the attribute in KSY where the error should be reported if reference is unknown
57+
*
58+
* @returns [[TypeNotFoundErr]] and/or [[EnumNotFoundErr]] error (several in case of `switch-on` type).
59+
*/
4760
private def resolveUserType(curClass: ClassSpec, dataType: DataType, path: List[String]): Iterable[CompilationProblem] = {
4861
dataType match {
4962
case ut: UserType =>

0 commit comments

Comments
 (0)