mbeckerle commented on a change in pull request #488:
URL: https://github.com/apache/incubator-daffodil/pull/488#discussion_r574862467



##########
File path: 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
     qnameInit
   }
 
+  /**
+   * We want to convert a choiceDispatchKey expression into C struct dot
+   * notation (rootElement->[subElement.field]) which will access the C
+   * struct field containing the choiceDispatchKey's runtime value.
+   *
+   * We make some assumptions to make generating the dot notation easier:
+   * - the expression starts with '{xs:string( and ends with )}'
+   * - the expression returns the value of a previous element without
+   *   changing the value in any way (except converting it to xs:string)
+   * - both the expression and the C code use only local names (for now...)
+   * - we can map the context node's path to a Unix-like slash path
+   * - all dpath operations look like Unix-like relative paths (../tag)
+   * - we can normalize the new path and convert it to C struct dot notation
+   * - we can store the accessed value in an int64_t local variable safely
+   */
+  private def choiceDispatchField(context: ElementBase): String = {
+    // We want to use SchemaComponent.scPath but it's private so duplicate it 
here (for now...)
+    def scPath(sc: SchemaComponent): Seq[SchemaComponent] = 
sc.optLexicalParent.map { scPath }.getOrElse(Nil) :+ sc
+    val localNames = scPath(context).map {
+      case er: AbstractElementRef => er.refQName.local
+      case e: ElementBase => e.namedQName.local
+      case ed: GlobalElementDecl => ed.namedQName.local
+      case _ => ""
+    }
+    val absoluteSlashPath = localNames.mkString("/")
+    val dispatchSlashPath = context.complexType.modelGroup match {
+      case choice: Choice if choice.isDirectDispatch =>
+        val expr = choice.choiceDispatchKeyEv.expr.toBriefXML()
+        val before = "'{xs:string("
+        val after = ")}'"
+        val relativePath = if (expr.startsWith(before) && expr.endsWith(after))
+          expr.substring(before.length, expr.length - after.length) else expr
+        val normalizedURI = new URI(absoluteSlashPath + "/" + 
relativePath).normalize
+        normalizedURI.getPath.substring(1)
+      case _ => ""
+    }
+    // Strip namespace prefixes since C code uses only local names (for now...)
+    val localDispatchSlashPath = dispatchSlashPath.replaceAll("/[^:]+:", "/")
+    val res = localDispatchSlashPath.replace('/', '.')
+    res
+  }
+
+  def addBeforeSwitchStatements(context: ElementBase): Unit = {
+    val erd = erdName(context)
+    val initStatement = s"    instance->_base.erd = &$erd;"
+
+    structs.top.initStatements += initStatement
+
+    val dispatchField = choiceDispatchField(context)
+    if (dispatchField.nonEmpty) {
+      val C = localName(context)
+      val declaration =
+        s"""    size_t      _choice; // choice of which union field to use
+           |    union
+           |    {""".stripMargin
+      val erdDef =
+        s"""static const ERD _choice_$erd = {
+           |    {
+           |        NULL, // namedQName.prefix
+           |        "_choice", // namedQName.local
+           |        NULL, // namedQName.ns
+           |    },
+           |    CHOICE, // typeCode
+           |    0, NULL, NULL, NULL, NULL, NULL, NULL
+           |};
+           |""".stripMargin
+      val offsetComputation = s"    (const char 
*)&${C}_compute_offsets._choice - (const char *)&${C}_compute_offsets"
+      val erdComputation = s"    &_choice_$erd"
+      val initStatement = s"    instance->_choice = NO_CHOICE;"
+      val initChoiceStatement =
+        s"""    int64_t key = rootElement->$dispatchField;
+           |    switch (key)
+           |    {""".stripMargin
+      val parseStatement =
+        s"""    instance->_base.erd->initChoice(&instance->_base, 
rootElement());
+           |    switch (instance->_choice)
+           |    {""".stripMargin
+      val unparseStatement =
+        s"""    instance->_base.erd->initChoice(&instance->_base, 
rootElement());
+           |    switch (instance->_choice)
+           |    {""".stripMargin
+
+      erds += erdDef
+      structs.top.declarations += declaration
+      structs.top.offsetComputations += offsetComputation
+      structs.top.erdComputations += erdComputation
+      structs.top.initStatements += initStatement
+      structs.top.initChoiceStatements += initChoiceStatement
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+  }
+
+  def addAfterSwitchStatements(): Unit = {
+    if (structs.top.initChoiceStatements.nonEmpty) {
+      val declaration = s"    };"
+      val initChoiceStatement =
+        s"""    default:
+           |        instance->_choice = NO_CHOICE;
+           |        break;
+           |    }
+           |
+           |    if (instance->_choice != NO_CHOICE)
+           |    {
+           |        const size_t choice = instance->_choice + 1; // skip the 
_choice field
+           |        const size_t offset = instance->_base.erd->offsets[choice];
+           |        const ERD *  childERD = 
instance->_base.erd->childrenERDs[choice];
+           |        InfosetBase *childNode = (InfosetBase *)((const char 
*)instance + offset);
+           |        childNode->erd = childERD;
+           |        return true;
+           |    }
+           |    else
+           |    {
+           |        return false;
+           |    }""".stripMargin
+      val parseStatement =
+        s"""    default:
+           |        pstate->error_msg = "node's _choice field was not 
initialized during parsing";

Review comment:
       This kind of error - choice not getting initialized properly. Is not a 
DFDL "processing error" in the formal DFDL sense that it should cause parser 
backtracking or result in a "Parse Error" message.
   It's an internal code invariant failure. It indicates the runtime2 system 
has a bug and is not initializing things properly. 
   These kinds of errors are strongly distinguished throughout the Daffodil 
code base. We should continue that in the runtime2 code. 
   I think you should just call a global invariantFailed("bug - choice field 
not initialized") function. That should probably just abort/throw/longjmp out 
and end processing. In principle, once the runtime2 code is correct, this code 
path should not be even be reachable.  




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to