stevedlawrence commented on a change in pull request #473:
URL: https://github.com/apache/incubator-daffodil/pull/473#discussion_r560992240



##########
File path: 
daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
##########
@@ -77,30 +76,52 @@ final class SetVariableUnparser(
 
 }
 
+final class NewVariableInstanceSuspendableExpression(
+  override val expr: CompiledExpression[AnyRef],
+  override val rd: VariableRuntimeData)
+  extends SuspendableExpression {
+
+  override protected def processExpressionResult(ustate: UState, v: 
DataValuePrimitive): Unit = {
+    ustate.variableMap.newVariableInstance(rd, v)
+  }
+
+  override protected def maybeKnownLengthInBits(ustate: UState) = MaybeULong(0)
+}
+
 // When implemented this almost certainly wants to be a combinator
 // Not two separate unparsers.
-class NewVariableInstanceStartUnparser(override val context: RuntimeData)
+class NewVariableInstanceStartUnparser(override val context: 
VariableRuntimeData)
   extends PrimUnparserNoData {
 
   override lazy val runtimeDependencies = Vector()
 
   override lazy val childProcessors = Vector()
 
+  def suspendableExpression = {
+    Assert.invariant(context.maybeDefaultValueExpr.isDefined)
+    new 
NewVariableInstanceSuspendableExpression(context.maybeDefaultValueExpr.get, 
context)
+  }
+
   override def unparse(state: UState) = {
-    val vrd = context.asInstanceOf[VariableRuntimeData]
-    state.newVariableInstance(vrd)
+    if (context.maybeDefaultValueExpr.isDefined) {
+      suspendableExpression.run(state)
+    }
+    else {
+      state.variableMap.newVariableInstance(context)
+    }
   }
 }
 
-class NewVariableInstanceEndUnparser(override val context: RuntimeData)
+class NewVariableInstanceEndUnparser(override val context: VariableRuntimeData)
   extends PrimUnparserNoData {
 
   override lazy val runtimeDependencies = Vector()
 
   override lazy val childProcessors = Vector()
 
   override def unparse(state: UState) = {
-    state.removeVariableInstance(context.asInstanceOf[VariableRuntimeData])
+    if (state.variableMap.find(context.globalQName).isDefined)

Review comment:
       In what case coudl the variable not exist?
   
   Is what's going on here is the newVariableInstance suspends and so hasn't 
been created yet. We continue unparsing but because that instance wasn't 
created so we have nothing to create so nothing to delete?
   
   But, say we had two nested new variable instances. Say the outer one doesn't 
suspend and the inner one does. In that case, the outer 
NewVariableInstanceStartUnparser adds a new variable instance just fine. Then 
we call NewVariableInstanceStartUnparser for the inner variable instance. That 
inner NVI suspends and so doesn't create the variable isntance until much 
later. Unparse will continue and we'll call NewVariableInstanceEndUnparser for 
the inner one. Note that we still haven't created the outer new variable 
instance. But we do have a new variable instance from the outter NVI. So this 
find() will work, and we'll reomve a variable instance. But we'll remove the 
outer NVI, which this should only remove the inner NVI (which doesn't exist 
yet). So now variable access will likely fail because we've removed our 
instance.
   
   Does that all sound like what would happen, or am I missing something 
subtle? If that is right, then it probably leads to my previous comment that we 
do need to always create an NVI without an value, and then suspensions can 
modify those values.

##########
File path: 
daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
##########
@@ -2078,4 +2080,268 @@
     </tdml:infoset>
   </tdml:parserTestCase>
 
+
+  <tdml:defineSchema name="variable_direction_schema">
+    <xs:include 
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format ref="ex:GeneralFormat"
+      representation="binary"/>
+
+    <dfdl:defineVariable name="remainingAddr" type="xs:string" 
dfdlx:direction="unparseOnly" />
+    <xs:element name="root">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:remainingAddr" defaultValue="{ 
ex:IPsrc }" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="byte1" type="xs:unsignedByte" 
dfdl:outputValueCalc="{ xs:unsignedByte(fn:substring-before($ex:remainingAddr, 
'.')) }" />
+          <xs:sequence>
+            <xs:annotation>
+              <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                <dfdl:newVariableInstance ref="ex:remainingAddr" 
defaultValue="{ fn:substring-after($ex:remainingAddr, '.') }" />
+              </xs:appinfo>
+            </xs:annotation>
+            <xs:element name="byte2" type="xs:unsignedByte" 
dfdl:outputValueCalc="{ xs:unsignedByte(fn:substring-before($ex:remainingAddr, 
'.')) }" />
+            <xs:sequence>
+              <xs:annotation>
+                <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                  <dfdl:newVariableInstance ref="ex:remainingAddr" 
defaultValue="{ fn:substring-after($ex:remainingAddr, '.') }" />
+                </xs:appinfo>
+              </xs:annotation>
+              <xs:element name="byte3" type="xs:unsignedByte" 
dfdl:outputValueCalc="{ xs:unsignedByte(fn:substring-before($ex:remainingAddr, 
'.')) }" />
+              <xs:sequence>
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                    <dfdl:newVariableInstance ref="ex:remainingAddr" 
defaultValue="{ fn:substring-after($ex:remainingAddr, '.') }" />
+                  </xs:appinfo>
+                </xs:annotation>
+                <xs:element name="byte4" type="xs:unsignedByte" 
dfdl:outputValueCalc="{ xs:unsignedByte($ex:remainingAddr) }" />
+              </xs:sequence>
+            </xs:sequence>
+          </xs:sequence>
+          <xs:element name="IPsrc" type="xs:string" dfdl:lengthKind="explicit" 
dfdl:length="7"
+            dfdl:inputValueCalc="{ fn:concat(../ex:byte1, '.', ../ex:byte2, 
'.', ../ex:byte3, '.', ../ex:byte4) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>

Review comment:
       I think it might be worth adding  anew test cases. SImilar to this where 
we have nested NVI's, but where we access the variable after the each instance 
goes out of scope. That way we can ensure that suspensions are happening 
correctly and that we are properly removing NVI's from scopes correctly.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -312,7 +314,7 @@ class VariableMap private(vTable: Map[GlobalQName, 
ArrayBuffer[VariableInstance]
   def find(qName: GlobalQName): Option[VariableInstance] = {
     val optBuf = vTable.get(qName)
     val variab = {
-      if (optBuf.isDefined)
+      if (optBuf.isDefined && !optBuf.get.isEmpty)

Review comment:
       This feels like it shouldn't be needed and is maybe a sign that we're 
doing somethign wrong?

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -344,6 +346,16 @@ class VariableMap private(vTable: Map[GlobalQName, 
ArrayBuffer[VariableInstance]
    */
   def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE, 
maybeState: Maybe[ParseOrUnparseState]): DataValuePrimitive = {
     val varQName = vrd.globalQName
+    if (maybeState.isDefined) {
+      vrd.direction match {
+        case VariableDirection.ParseOnly if 
(!maybeState.get.isInstanceOf[PState]) =>
+          maybeState.get.SDW("Attempting to read variable %s which is marked 
as parseOnly during unparsing".format(varQName))
+        case VariableDirection.UnparseOnly if 
(!maybeState.get.isInstanceOf[UState]) =>
+          maybeState.get.SDW("Attempting to read variable %s which is marked 
as unparseOnly during parsing".format(varQName))
+        case _ => // Do nothing

Review comment:
       Should these be SDEs?

##########
File path: 
daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
##########
@@ -77,30 +76,52 @@ final class SetVariableUnparser(
 
 }
 
+final class NewVariableInstanceSuspendableExpression(
+  override val expr: CompiledExpression[AnyRef],
+  override val rd: VariableRuntimeData)
+  extends SuspendableExpression {
+
+  override protected def processExpressionResult(ustate: UState, v: 
DataValuePrimitive): Unit = {
+    ustate.variableMap.newVariableInstance(rd, v)
+  }
+
+  override protected def maybeKnownLengthInBits(ustate: UState) = MaybeULong(0)
+}
+

Review comment:
       Just curious, can you talk about the reason for this? Is this needed 
because of the new direction stuff, or is it needed to fix a preexisting 
newVariableInstance bug that this just happened to unconver?
   
   Also, I'm unsure if this is right. This doesn't create a newVariableInstance 
until *after* the expression result is evaluated. Which means this new instance 
could not be created for a very long time while we wait for the expression 
result to be evaluated. So what happens if a parser accesses that variable 
before this suspendable expression evaluates and creates a new instance? Is it 
going to access the old instance?
   
   Almost seems like we need to create a newVariableInstance immediately but 
without a value. And any accesses of that new instance need to suspend until 
after this suspension evaluates and sets this value. I guess things could get 
complicated because a setVariable could also try to change the value of this 
newVariableInstance, and it too could suspend. So there needs to be some logic 
about how which suspension gets precedence.
   
   Or maybe I'm over compilicating this all?




----------------------------------------------------------------
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