Skip to content

Commit

Permalink
Merge pull request #283 from kaitai-io/keep-root-and-parent-local
Browse files Browse the repository at this point in the history
Pass `_root` and `_parent` to all local and no external types
  • Loading branch information
generalmimon authored Mar 30, 2024
2 parents 15959f0 + e502eca commit 29f7a59
Show file tree
Hide file tree
Showing 17 changed files with 65 additions and 30 deletions.
4 changes: 2 additions & 2 deletions shared/src/main/scala/io/kaitai/struct/ClassCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ class ClassCompiler(
curClass.params.foreach((paramDefSpec) =>
paramDefSpec.dataType match {
case ut: UserType =>
val externalTypeName = ut.classSpec.get.name
if (externalTypeName.head != curClass.name.head) {
if (ut.isExternal(curClass)) {
val externalTypeName = ut.classSpec.get.name
lang.classForwardDeclaration(externalTypeName)
}
case _ => // no forward declarations needed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ object DataType {
var args: Seq[Ast.expr]
) extends StructType {
var classSpec: Option[ClassSpec] = None
/**
* Determines whether the user type represented by this `UserType` instance
* is external from the perspective of the given `ClassSpec` in which it is
* used (via `seq`, `instances` or `params`).
* @param curClass class spec from which the local/external relationship
* should be evaluated
*/
def isExternal(curClass: ClassSpec): Boolean =
classSpec.get.isExternal(curClass)
def isOpaque = {
val cs = classSpec.get
cs.isTopLevel || cs.meta.isOpaque
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 @@ -78,6 +78,16 @@ case class ClassSpec(

def parentType: DataType = parentClass.toDataType

/**
* Determines whether this `ClassSpec` represents a type that is external
* (i.e. not defined in the same .ksy file) from the perspective of the given
* `ClassSpec`.
* @param curClass class spec from which the local/external relationship
* should be evaluated
*/
def isExternal(curClass: ClassSpec): Boolean =
name.head != curClass.name.head

/**
* Recursively traverses tree of types starting from this type, calling
* certain function for every type, starting from this one.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ class CSharpCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
s"$io.ReadBitsInt${Utils.upperCamelCase(bitEndian.toSuffix)}($width)"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), "", ", ", ", ")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ class CppCompiler(
s"$io->read_bits_int_${bitEndian.toSuffix}($width)"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), "", ", ", ", ")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
case BitsType(width: Int, bitEndian) =>
s"$io.ReadBitsInt${Utils.upperCamelCase(bitEndian.toSuffix)}($width)"
case t: UserType =>
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
case BitsType(width: Int, bitEndian) =>
s"$io.readBitsInt${Utils.upperCamelCase(bitEndian.toSuffix)}($width)"
case t: UserType =>
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,16 @@ class JavaScriptCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
case BitsType(width: Int, bitEndian) =>
s"$io.readBitsInt${Utils.upperCamelCase(bitEndian.toSuffix)}($width)"
case t: UserType =>
val parent = t.forcedParent match {
case Some(USER_TYPE_NO_PARENT) => "null"
case Some(fp) => translator.translate(fp)
case None => "this"
val (parent, root) = if (t.isExternal(typeProvider.nowClass)) {
("null", "null")
} else {
val parent = t.forcedParent match {
case Some(USER_TYPE_NO_PARENT) => "null"
case Some(fp) => translator.translate(fp)
case None => "this"
}
(parent, "this._root")
}
val root = if (t.isOpaque) "null" else "this._root"
val addEndian = t.classSpec.get.meta.endian match {
case Some(InheritedEndian) => ", this._is_le"
case _ => ""
Expand Down Expand Up @@ -547,7 +551,7 @@ class JavaScriptCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
}

override def classToString(toStringExpr: Ast.expr): Unit = {
val className = type2class(translator.provider.nowClass.name.last)
val className = type2class(typeProvider.nowClass.name.last)

out.puts
out.puts(s"${className}.prototype.toString = function() {")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ class LuaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
s"$io:read_bits_int_${bitEndian.toSuffix}($width)"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), "", ", ", ", ")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,20 +403,24 @@ class NimCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
case BitsType(width: Int, bitEndian) =>
s"$io.readBitsInt${camelCase(bitEndian.toSuffix, true)}($width)"
case t: UserType =>
val addArgs = {
val (parent, root) = if (t.isExternal(typeProvider.nowClass)) {
("nil", "nil")
} else {
val parent = t.forcedParent match {
case Some(USER_TYPE_NO_PARENT) => "nil"
case Some(fp) => translator.translate(fp)
case None => "this"
}
s", this.root, $parent"
(parent, "this.root")
}
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), ", ", ", ", "")
val concreteName = namespaced(t.classSpec match {
case Some(cs) => cs.name
case None => t.name
})
s"${concreteName}.read($io$addArgs$addParams)"
// FIXME: apparently, the Nim compiler uses a different order of
// `$parent` and `$root` than literally every other language
s"${concreteName}.read($io, $root, $parent$addParams)"
}

if (assignType != dataType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
s"$io->readBitsInt${Utils.upperCamelCase(bitEndian.toSuffix)}($width)"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), "", ", ", ", ")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ class PerlCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
case BitsType(width: Int, bitEndian) =>
s"$io->read_bits_int_${bitEndian.toSuffix}($width)"
case t: UserType =>
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
s"$io.read_bits_int_${bitEndian.toSuffix}($width)"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), "", ", ", ", ")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down Expand Up @@ -502,9 +502,8 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)

def userType2class(t: UserType): String = {
val name = t.classSpec.get.name
val firstName = name.head
val prefix = if (t.isOpaque && firstName != translator.provider.nowClass.name.head) {
s"$firstName."
val prefix = if (t.isExternal(typeProvider.nowClass)) {
s"${name.head}."
} else {
""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ class RubyCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
s"$io.read_bits_int_${bitEndian.toSuffix}($width)"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), ", ", ", ", "")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
s"$io.read_bits_int($width)?"
case t: UserType =>
val addParams = Utils.join(t.args.map((a) => translator.translate(a)), "", ", ", ", ")
val addArgs = if (t.isOpaque) {
val addArgs = if (t.isExternal(typeProvider.nowClass)) {
""
} else {
val parent = t.forcedParent match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ class ParentTypes(classSpecs: ClassSpecs) {
}

def markupParentAs(curClass: ClassSpec, ut: UserType): Unit = {
Log.typeProcParent.info(() => s"..... class=$ut has parent=${curClass.nameAsStr}")
ut.classSpec match {
case Some(usedClass) =>
markupParentAs(curClass, usedClass)
Expand All @@ -79,6 +78,12 @@ class ParentTypes(classSpecs: ClassSpecs) {
}

def markupParentAs(parent: ClassSpec, child: ClassSpec): Unit = {
// Don't allow type usages across spec boundaries to affect parent resolution
if (child.isExternal(parent)) {
Log.typeProcParent.info(() => s"..... cross-spec usage of class=${child.nameAsStr} from parent=${parent.nameAsStr} ignored")
return
}
Log.typeProcParent.info(() => s"..... class=${child.nameAsStr} has parent=${parent.nameAsStr}")
child.parentClass match {
case UnknownClassSpec =>
child.parentClass = parent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,16 @@ class GoTranslator(out: StringLanguageOutputWriter, provider: TypeProvider, impo

def userType(t: UserType, io: String) = {
val v = allocateLocalVar()
val parent = t.forcedParent match {
case Some(USER_TYPE_NO_PARENT) => "nil"
case Some(fp) => translate(fp)
case None => "this"
val (parent, root) = if (t.isExternal(provider.nowClass)) {
("nil", "nil")
} else {
val parent = t.forcedParent match {
case Some(USER_TYPE_NO_PARENT) => "nil"
case Some(fp) => translate(fp)
case None => "this"
}
(parent, "this._root")
}
val root = if (t.isOpaque) "nil" else "this._root"
val addParams = t.args.map((a) => translate(a)).mkString(", ")
out.puts(s"${localVarName(v)} := New${GoCompiler.types2class(t.classSpec.get.name)}($addParams)")
out.puts(s"err = ${localVarName(v)}.Read($io, $parent, $root)")
Expand Down

0 comments on commit 29f7a59

Please sign in to comment.