Skip to content

Commit

Permalink
Fix leaking of types and enums from other imported specs to specs whi…
Browse files Browse the repository at this point in the history
…ch does not import them explicitly
  • Loading branch information
Mingun committed Oct 5, 2024
1 parent 420d877 commit 2c0967d
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,12 @@ class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends
case Some(upClass) => resolveTypeName(upClass, typeName)
case None =>
classSpecs.get(typeName) match {
case Some(spec) => spec
case None =>
// We should use that spec if it is imported in our file (which is represented
// by our top-level class). If `topClass` imports `classSpec`, we could try to
// resolve type in it
// TODO: if type is defined in spec, we could add a suggestion to error to add missing import
case Some(spec) if (topClass.imports.contains(spec)) => spec
case _ =>
throw new TypeNotFoundInHierarchyError(typeName, nowClass)
}
}
Expand Down
10 changes: 10 additions & 0 deletions shared/src/main/scala/io/kaitai/struct/format/ClassSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ case class ClassSpec(

var seqSize: Sized = NotCalculatedSized

/**
* The list of top-level type specifications which is imported to the file,
* which top-level type is represented by this class.
*
* This collection filled only for top-level classes (for which [[upClass]] is `None`).
*
* This collection is filled by the [[io.kaitai.struct.precompile.LoadImports]] pass.
*/
var imports = mutable.ListBuffer[ClassSpec]()

def toDataType: DataType = {
val cut = CalcUserType(name, None)
cut.classSpec = Some(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class LoadImports(specs: ClassSpecs) {
loadImport(
name,
curClass.meta.path ++ List("imports", idx.toString),
Some(curClass.fileNameAsStr),
curClass,
workDir
)
}).map((x) => x.flatten)
Expand All @@ -44,9 +44,10 @@ class LoadImports(specs: ClassSpecs) {
Future.sequence(List(thisMetaFuture, nestedFuture)).map((x) => x.flatten)
}

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

val inFile = Some(curClass.fileNameAsStr)
val impPath = ImportPath.fromString(name)
val fullPath = ImportPath.add(workDir, impPath)

Expand All @@ -63,8 +64,9 @@ class LoadImports(specs: ClassSpecs) {
s".. LoadImports: loadImport($name, workDir = $workDir), got spec=$specNameAsStr"
})
optSpec match {
case Some(spec) =>
val specName = spec.name.head
case Some(importedSpec) =>
curClass.imports += importedSpec
val specName = importedSpec.name.head
// Check if spec name does not match file name. If it doesn't match,
// it is probably already a serious error.
if (name != specName)
Expand All @@ -88,12 +90,12 @@ class LoadImports(specs: ClassSpecs) {
val isNewSpec = specs.synchronized {
val isNew = !specs.contains(specName)
if (isNew) {
specs(specName) = spec
specs(specName) = importedSpec
}
isNew
}
if (isNewSpec) {
processClass(spec, ImportPath.updateWorkDir(workDir, impPath))
processClass(importedSpec, ImportPath.updateWorkDir(workDir, impPath))
} else {
Log.importOps.warn(() => s"... we have that already, ignoring")
Future { List() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import io.kaitai.struct.problems._
/**
* A collection of methods that resolves user types and enum types, i.e.
* converts names into ClassSpec / EnumSpec references.
*
* This step runs for each top-level [[format.ClassSpec]].
*/
class ResolveTypes(specs: ClassSpecs, topClass: ClassSpec, opaqueTypes: Boolean) extends PrecompileStep {
/** Resolves references to types and enums in `topClass` and all its nested types. */
override def run(): Iterable[CompilationProblem] =
topClass.mapRec(resolveUserTypes).map(problem => problem.localizedInType(topClass))

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

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

0 comments on commit 2c0967d

Please sign in to comment.