You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ja...@apache.org on 2021/03/15 14:22:29 UTC

[daffodil] branch master updated: Implement variable direction property

This is an automated email from the ASF dual-hosted git repository.

jadams pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new f1603a0  Implement variable direction property
f1603a0 is described below

commit f1603a0da8c9610f7ade79250315a2b5db333db2
Author: Josh Adams <ja...@tresys.com>
AuthorDate: Wed Dec 9 10:47:29 2020 -0500

    Implement variable direction property
    
    This adds a 'direction' property to defineVariable statements with the
    following options:
     - both (default)
     - parseOnly
     - unparseOnly
    
    Current behavior is to create NadaParser/Unparser's for setVariable and
    newVariableInstance statements when we are processing in the opposite
    direction of the direction property while attempting to read a variable
    while going the opposite direction will trigger a warning.
    
    This commit also addresses several issues involving how
    newVariableInstances are handled during unparsing suspensions.
    
    DAFFODIL-2429
---
 .../apache/daffodil/dsom/DFDLDefineVariable.scala  |  10 +-
 .../grammar/primitives/ElementCombinator.scala     |   4 +-
 .../grammar/primitives/PrimitivesExpressions.scala |  48 +-
 .../externalvars/TestExternalVariablesLoader.scala |   4 +-
 .../org/apache/daffodil/xsd/DFDL_part3_model.xsd   |   1 +
 .../resources/org/apache/daffodil/xsd/dfdlx.xsd    |   9 +
 .../processors/unparsers/ElementUnparser.scala     |   4 +-
 .../unparsers/ExpressionEvaluatingUnparsers.scala  |  36 +-
 .../daffodil/debugger/InteractiveDebugger.scala    |   1 +
 .../org/apache/daffodil/dpath/DPathRuntime.scala   |   3 +-
 .../apache/daffodil/processors/RuntimeData.scala   |   6 +-
 .../daffodil/processors/SuspendableOperation.scala |   2 +-
 .../apache/daffodil/processors/VariableMap1.scala  | 132 ++++--
 .../parsers/ExpressionEvaluatingParsers.scala      |   5 +
 .../daffodil/processors/parsers/PState.scala       |   2 +-
 .../daffodil/processors/unparsers/UState.scala     |  12 +-
 .../daffodil/section07/variables/variables.tdml    | 524 ++++++++++++++++++++-
 .../section07/variables/TestVariables.scala        |  12 +
 18 files changed, 718 insertions(+), 97 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
index 41d7b97..e51bde1 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
@@ -28,6 +28,7 @@ import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.grammar.primitives.{ SetVariable, NewVariableInstanceStart, NewVariableInstanceEnd }
 import org.apache.daffodil.schema.annotation.props.Found
+import org.apache.daffodil.schema.annotation.props.gen.VariableDirection
 
 class DFDLDefineVariable(node: Node, doc: SchemaDocument)
   extends DFDLDefiningAnnotation(node, doc) {
@@ -40,6 +41,11 @@ class DFDLDefineVariable(node: Node, doc: SchemaDocument)
 
   final lazy val external = getAttributeOption("external").map { _.toBoolean }.getOrElse(false)
 
+  final lazy val direction = {
+    val directionStr = getAttributeOption(XMLUtils.DFDLX_NAMESPACE, "direction").getOrElse("both")
+    VariableDirection(directionStr, this)
+  }
+
   private lazy val defaultValueAsAttribute = getAttributeOption("defaultValue")
   private lazy val defaultValueAsElement = node.child.text.trim
 
@@ -83,6 +89,7 @@ class DFDLDefineVariable(node: Node, doc: SchemaDocument)
       this.path,
       this.namespaces,
       this.external,
+      this.direction,
       maybeDefaultValueExpr,
       this.typeQName,
       this.namedQName.asInstanceOf[GlobalQName],
@@ -116,7 +123,7 @@ final class DFDLNewVariableInstance(node: Node, decl: AnnotatedSchemaComponent)
 
   private lazy val attrValue = getAttributeOption("value")
 
-  private lazy val <dfdl:setVariable>{ eltChildren @ _* }</dfdl:setVariable> = node
+  private lazy val <dfdl:newVariableInstance>{ eltChildren @ _* }</dfdl:newVariableInstance> = node
 
   private lazy val eltValue = eltChildren.text.trim
 
@@ -159,6 +166,7 @@ final class DFDLNewVariableInstance(node: Node, decl: AnnotatedSchemaComponent)
       this.path,
       this.namespaces,
       defv.external,
+      defv.direction,
       maybeDefaultValueExpr,
       defv.typeQName,
       defv.namedQName.asInstanceOf[GlobalQName],
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
index fd50d50..3918f7f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
@@ -90,7 +90,7 @@ class ElementCombinator(
     subComb.parser
   }
 
-  private lazy val uSetVars = context.setVariableStatements.map(_.gram(context).unparser).toArray
+  private lazy val uSetVars = context.setVariableStatements.map(_.gram(context).unparser).toArray.filterNot { _.isEmpty }
 
   private lazy val eBeforeUnparser: Maybe[Unparser] =
     if (eBeforeContent.isEmpty) Maybe.Nope
@@ -359,7 +359,7 @@ abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eG
     }
   }
   lazy val patAssert = context.assertStatements.filter(_.testKind == TestKind.Pattern).map(_.gram(context).parser).toArray
-  lazy val pSetVar = context.setVariableStatements.map(_.gram(context).parser).toArray
+  lazy val pSetVar = context.setVariableStatements.map(_.gram(context).parser).toArray.filterNot { _.isEmpty }
   lazy val testDiscrim = {
     val td = context.discriminatorStatements.filter(_.testKind == TestKind.Expression)
     Assert.invariant(td.size <= 1)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
index fe5ec20..4c3075b 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
@@ -31,13 +31,16 @@ import org.apache.daffodil.dsom.ElementBase
 import org.apache.daffodil.processors.parsers.NewVariableInstanceEndParser
 import org.apache.daffodil.processors.parsers.SetVariableParser
 import org.apache.daffodil.processors.parsers.IVCParser
+import org.apache.daffodil.processors.parsers.NadaParser
 import org.apache.daffodil.processors.unparsers.SetVariableUnparser
 import org.apache.daffodil.processors.unparsers.NewVariableInstanceEndUnparser
 import org.apache.daffodil.processors.unparsers.NewVariableInstanceStartUnparser
+import org.apache.daffodil.processors.unparsers.NadaUnparser
 import org.apache.daffodil.compiler.ForParser
 import org.apache.daffodil.schema.annotation.props.PropertyLookupResult
 import org.apache.daffodil.schema.annotation.props.Found
 import org.apache.daffodil.schema.annotation.props.gen.FailureType
+import org.apache.daffodil.schema.annotation.props.gen.VariableDirection
 import org.apache.daffodil.dsom.ExpressionCompilers
 import org.apache.daffodil.dsom.DFDLSetVariable
 import org.apache.daffodil.dsom.DFDLNewVariableInstance
@@ -179,8 +182,19 @@ case class SetVariable(stmt: DFDLSetVariable)
 
   override lazy val nodeKind = stmt.defv.primType
 
-  lazy val parser: DaffodilParser = new SetVariableParser(expr, stmt.defv.runtimeData)
-  override lazy val unparser: DaffodilUnparser = new SetVariableUnparser(expr, stmt.defv.runtimeData, stmt.nonTermRuntimeData)
+  lazy val parser: DaffodilParser = {
+    if (stmt.defv.runtimeData.direction == VariableDirection.UnparseOnly)
+      new NadaParser(stmt.defv.runtimeData)
+    else
+      new SetVariableParser(expr, stmt.defv.runtimeData)
+  }
+
+  override lazy val unparser: DaffodilUnparser = {
+    if (stmt.defv.runtimeData.direction == VariableDirection.ParseOnly)
+      new NadaUnparser(stmt.defv.runtimeData)
+    else
+      new SetVariableUnparser(expr, stmt.defv.runtimeData, stmt.nonTermRuntimeData)
+  }
 }
 
 abstract class NewVariableInstanceBase(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
@@ -190,15 +204,37 @@ abstract class NewVariableInstanceBase(decl: AnnotatedSchemaComponent, stmt: DFD
 case class NewVariableInstanceStart(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
   extends NewVariableInstanceBase(decl, stmt) {
 
-  lazy val parser: DaffodilParser = new NewVariableInstanceStartParser(stmt.variableRuntimeData)
-  override lazy val unparser: DaffodilUnparser = new NewVariableInstanceStartUnparser(stmt.variableRuntimeData)
+  lazy val parser: DaffodilParser = {
+    if (stmt.defv.runtimeData.direction == VariableDirection.UnparseOnly)
+      new NadaParser(stmt.variableRuntimeData)
+    else
+      new NewVariableInstanceStartParser(stmt.variableRuntimeData)
+  }
+
+  override lazy val unparser: DaffodilUnparser = {
+    if (stmt.defv.runtimeData.direction == VariableDirection.ParseOnly)
+      new NadaUnparser(stmt.variableRuntimeData)
+    else
+      new NewVariableInstanceStartUnparser(stmt.variableRuntimeData)
+  }
 }
 
 case class NewVariableInstanceEnd(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
   extends NewVariableInstanceBase(decl, stmt) {
 
-  lazy val parser: DaffodilParser = new NewVariableInstanceEndParser(stmt.variableRuntimeData)
-  override lazy val unparser: DaffodilUnparser = new NewVariableInstanceEndUnparser(stmt.variableRuntimeData)
+  lazy val parser: DaffodilParser = {
+    if (stmt.defv.runtimeData.direction == VariableDirection.UnparseOnly)
+      new NadaParser(stmt.variableRuntimeData)
+    else
+      new NewVariableInstanceEndParser(stmt.variableRuntimeData)
+  }
+
+  override lazy val unparser: DaffodilUnparser = {
+    if (stmt.defv.runtimeData.direction == VariableDirection.ParseOnly)
+      new NadaUnparser(stmt.variableRuntimeData)
+    else
+      new NewVariableInstanceEndUnparser(stmt.variableRuntimeData)
+  }
 }
 
 /**
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala b/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
index 60e68d9..9d0e3bb 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
@@ -110,9 +110,9 @@ class TestExternalVariablesLoader extends Logging {
 
     // Verify that the external variables override the previous values
     // in the VariableMap
-    val value1 = vmap.readVariable(v_no_default_vrd, Fakes.fakeElem, Maybe.Nope)
+    val value1 = vmap.find(v_no_default_vrd.globalQName).get.value
     assertEquals(1, value1.getAnyRef)
-    val value2 = vmap.readVariable(v_with_default_vrd, Fakes.fakeElem, Maybe.Nope)
+    val value2 = vmap.find(v_with_default_vrd.globalQName).get.value
     assertEquals(2, value2.getAnyRef)
   }
 
diff --git a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
index 7b6b18d..f3c15b6 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
@@ -136,6 +136,7 @@
     <xsd:attribute name="external" type="xsd:boolean" />
     <xsd:attribute name="defaultValue"
       type="dfdl:DFDLStringLiteral_Or_DFDLExpression" />
+    <xsd:attribute ref="dfdlx:direction" />
   </xsd:attributeGroup>
 
   <xsd:attributeGroup name="SetVariableAG">
diff --git a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
index e463645..d253767 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
@@ -57,6 +57,15 @@
     </xs:restriction>
   </xs:simpleType>
 
+  <xs:attribute name="direction" type="dfdlx:VariableDirectionEnum"/>
+  <xs:simpleType name="VariableDirectionEnum">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="both" />
+      <xs:enumeration value="parseOnly" />
+      <xs:enumeration value="unparseOnly" />
+    </xs:restriction>
+  </xs:simpleType>
+
   <xs:attribute name="emptyElementParsePolicy" type="dfdlx:EmptyElementParsePolicyEnum"/>
   <xs:simpleType name="EmptyElementParsePolicyEnum">
     <xs:restriction base="xs:string">
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementUnparser.scala
index e224c10..3b8c648 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementUnparser.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementUnparser.scala
@@ -311,7 +311,7 @@ class ElementSpecifiedLengthUnparser(
 /**
  * For dfdl:outputValueCalc elements.
  */
-class ElementOVCSpecifiedLengthUnparserSuspendableExpresion(
+class ElementOVCSpecifiedLengthUnparserSuspendableExpression(
   callingUnparser: ElementOVCSpecifiedLengthUnparser)
   extends SuspendableExpression {
 
@@ -355,7 +355,7 @@ class ElementOVCSpecifiedLengthUnparser(
   override lazy val runtimeDependencies = maybeTargetLengthEv.toList.toVector
 
   private def suspendableExpression =
-    new ElementOVCSpecifiedLengthUnparserSuspendableExpresion(this)
+    new ElementOVCSpecifiedLengthUnparserSuspendableExpression(this)
 
   Assert.invariant(context.outputValueCalcExpr.isDefined)
 
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
index d0b7e0f..c237eeb 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
@@ -27,10 +27,11 @@ import org.apache.daffodil.infoset.Infoset
 import org.apache.daffodil.processors.ElementRuntimeData
 import org.apache.daffodil.processors.Evaluatable
 import org.apache.daffodil.processors.NonTermRuntimeData
-import org.apache.daffodil.processors.RuntimeData
 import org.apache.daffodil.processors.Success
 import org.apache.daffodil.processors.TypeCalculator
 import org.apache.daffodil.processors.VariableRuntimeData
+import org.apache.daffodil.processors.{ VariableInProcess, VariableDefined }
+//import org.apache.daffodil.processors.SuspendableOperation
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.MaybeULong
 
@@ -77,9 +78,24 @@ 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 = {
+    val vi = ustate.variableMap.find(rd.globalQName)
+    Assert.invariant(vi.isDefined)
+    vi.get.setState(VariableDefined)
+    vi.get.setValue(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()
@@ -87,21 +103,25 @@ class NewVariableInstanceStartUnparser(override val context: RuntimeData)
   override lazy val childProcessors = Vector()
 
   override def unparse(state: UState) = {
-    val vrd = context.asInstanceOf[VariableRuntimeData]
-    state.newVariableInstance(vrd)
+    state.variableMap.newVariableInstance(context)
+    if (context.maybeDefaultValueExpr.isDefined) {
+      val maybeVar = state.variableMap.find(context.globalQName)
+      Assert.invariant(maybeVar.isDefined)
+      maybeVar.get.setState(VariableInProcess)
+      val suspendableExpression = new NewVariableInstanceSuspendableExpression(context.maybeDefaultValueExpr.get, context)
+      suspendableExpression.run(state)
+    }
   }
 }
 
-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])
-  }
+  override def unparse(state: UState) = state.variableMap.removeVariableInstance(context)
 }
 
 class TypeValueCalcUnparser(typeCalculator: TypeCalculator, repTypeUnparser: Unparser, e: ElementRuntimeData, repTypeRuntimeData: ElementRuntimeData)
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
index 7ba02d4..42dfb0c 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
@@ -1745,6 +1745,7 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
             case VariableRead => "read"
             case VariableSet => "set"
             case VariableUndefined => "undefined"
+            case VariableBeingDefined => "being defined"
             case VariableInProcess => "in process"
           }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
index 9a59566..edd51e3 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
@@ -179,7 +179,8 @@ case class VRef(vrd: VariableRuntimeData, context: ThrowsSDE)
 
   override def run(dstate: DState): Unit = {
     Assert.invariant(dstate.vmap != null)
-    dstate.setCurrentValue(dstate.vmap.readVariable(vrd, context, dstate.parseOrUnparseState))
+    Assert.invariant(dstate.parseOrUnparseState.isDefined)
+    dstate.setCurrentValue(dstate.vmap.readVariable(vrd, context, dstate.parseOrUnparseState.get))
   }
 
   override def toXML = toXML("$" + vrd.globalQName.toPrettyString)
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
index 6519e73..00ef060 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
@@ -35,6 +35,7 @@ import org.apache.daffodil.infoset.PartialNextElementResolver
 import org.apache.daffodil.schema.annotation.props.gen.BitOrder
 import org.apache.daffodil.schema.annotation.props.gen.Representation
 import org.apache.daffodil.schema.annotation.props.gen.YesNo
+import org.apache.daffodil.schema.annotation.props.gen.VariableDirection
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe.Nope
 import org.apache.daffodil.util.PreSerialization
@@ -952,6 +953,7 @@ final class VariableRuntimeData(
   pathArg: String,
   namespacesArg: NamespaceBinding,
   val external: Boolean,
+  val direction: VariableDirection,
   @TransientParam maybeDefaultValueExprArg: => Maybe[CompiledExpression[AnyRef]],
   val typeRef:  RefQName,
   val globalQName: GlobalQName,
@@ -999,8 +1001,6 @@ final class VariableRuntimeData(
     VariableInstance(state, defaultValue, this, maybeDefaultValueExpr)
   }
 
-  def createVariableInstance(): VariableInstance = {
-    VariableInstance(state, value, this, maybeDefaultValueExpr)
-  }
+  def createVariableInstance(): VariableInstance = VariableInstance(state, value, this, maybeDefaultValueExpr)
 
 }
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SuspendableOperation.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SuspendableOperation.scala
index ae9d91a..95a3ed5 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SuspendableOperation.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SuspendableOperation.scala
@@ -37,7 +37,7 @@ import org.apache.daffodil.infoset.RetryableException
 trait SuspendableOperation
   extends Suspension {
 
-  override def rd: TermRuntimeData
+  override def rd: RuntimeData
 
   override def toString = "%s for %s".format(Misc.getNameFromClass(this), rd.diagnosticDebugName)
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
index 0674cf5..958ba3d 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
@@ -33,6 +33,8 @@ import org.apache.daffodil.util.PreSerialization
 import org.apache.daffodil.util.TransientParam
 import org.apache.daffodil.xml.{GlobalQName, UnspecifiedNamespace, NamedQName, RefQName}
 import org.apache.daffodil.processors.parsers.PState
+import org.apache.daffodil.processors.unparsers.UState
+import org.apache.daffodil.schema.annotation.props.gen.VariableDirection
 
 import scala.collection.mutable.{Map, ArrayBuffer}
 
@@ -42,6 +44,11 @@ case object VariableUndefined extends VariableState
 
 case object VariableDefined extends VariableState
 
+/**
+ * Used to detect circular definitions of variables
+ */
+case object VariableBeingDefined extends VariableState
+
 case object VariableSet extends VariableState
 
 case object VariableRead extends VariableState
@@ -141,19 +148,22 @@ class VariableInstance private (
     this._value = Some(v)
   }
 
+  def setDefaultValue(v: DataValuePrimitiveNullable) = this._value = Some(v)
+
   override def preSerialization: Unit = {
     defaultValueExpr
     value
     state
   }
 
-  override def toString: String = "VariableInstance(%s,%s,%s,%s,%s,%s".format(
+  override def toString: String = "VariableInstance(%s,%s,%s,%s,%s,%s,%s)".format(
                                                     state,
                                                     value,
                                                     rd,
                                                     defaultValueExpr,
                                                     priorState,
-                                                    priorValue)
+                                                    priorValue,
+                                                    this.hashCode)
 
   def copy(
     state: VariableState = state,
@@ -211,6 +221,10 @@ class VariableHasNoValue(qname: NamedQName, context: VariableRuntimeData) extend
   "Variable map (runtime): variable %s has no value. It was not set, and has no default value.".format(qname))
   with RetryableException
 
+class VariableSuspended(qname: NamedQName, context: VariableRuntimeData) extends VariableException(qname, context,
+  "Variable map (runtime): variable %s is currently suspended".format(qname))
+  with RetryableException
+
 /**
  * This expression can be thrown either at the start of parsing is the
  * expressions in a defineVariable are circular, or later during parsing if
@@ -244,6 +258,8 @@ final class VariableBox(initialVMap: VariableMap) {
   def setVMap(newMap: VariableMap): Unit = {
     vmap_ = newMap
   }
+
+  def cloneForSuspension(): VariableBox = new VariableBox(vmap.topLevelInstances)
 }
 
 /**
@@ -269,9 +285,9 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
     this(Map(topLevelVRDs.map {
       vrd =>
         val variab = vrd.createVariableInstance()
-        val abuf = new ArrayBuffer[VariableInstance]
-        abuf += variab
-        (vrd.globalQName, abuf)
+        val variableInstances = new ArrayBuffer[VariableInstance]
+        variableInstances += variab
+        (vrd.globalQName, variableInstances)
     }: _*))
 
   override def toString(): String = {
@@ -283,8 +299,18 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
    * VariableInstances are mutable and cannot safely be shared across threads
    */
   def copy(): VariableMap = {
-    val table = vTable.map { case (k: GlobalQName, abuf: ArrayBuffer[VariableInstance]) => {
-      val newBuf = abuf.map { _.copy() }
+    val table = vTable.map { case (k: GlobalQName, variableInstances: ArrayBuffer[VariableInstance]) => {
+      val newBuf = variableInstances.map { _.copy() }
+      (k, newBuf)
+    }}
+
+    new VariableMap(table)
+  }
+
+  def topLevelInstances(): VariableMap = {
+    val table = vTable.map { case (k: GlobalQName, variableInstances: ArrayBuffer[VariableInstance]) => {
+      val newBuf = new ArrayBuffer[VariableInstance]()
+      newBuf.append(variableInstances.last)
       (k, newBuf)
     }}
 
@@ -297,7 +323,7 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
   // must also ensure that the expressions only reference other variables that
   // have default value expressions or are defined externally.
   def forceExpressionEvaluations(state: ParseOrUnparseState): Unit = {
-    vTable.foreach { case (_, abuf) => { abuf.foreach { inst => {
+    vTable.foreach { case (_, variableInstances) => { variableInstances.foreach { inst => {
       (inst.state, inst.value, inst.defaultValueExpr.isDefined) match {
         // Evaluate defineVariable statements with non-constant default value expressions
         case (VariableDefined, DataValue.NoValue, true) => {
@@ -342,16 +368,24 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
    * Returns the value of a variable and sets the state of the variable to be
    * VariableRead.
    */
-  def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE, maybeState: Maybe[ParseOrUnparseState]): DataValuePrimitive = {
+  def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE, pstate: ParseOrUnparseState): DataValuePrimitive = {
     val varQName = vrd.globalQName
-    val abuf = vTable.get(varQName)
-    if (abuf.isDefined) {
-      val variable = abuf.get.last
+    vrd.direction match {
+      case VariableDirection.ParseOnly if (!pstate.isInstanceOf[PState]) =>
+        pstate.SDE("Attempting to read variable %s which is marked as parseOnly during unparsing".format(varQName))
+      case VariableDirection.UnparseOnly if (!pstate.isInstanceOf[UState]) =>
+        pstate.SDE("Attempting to read variable %s which is marked as unparseOnly during parsing".format(varQName))
+      case _ => // Do nothing
+    }
+
+    val variableInstances = vTable.get(varQName)
+    if (variableInstances.isDefined) {
+      val variable = variableInstances.get.last
       variable.state match {
         case VariableRead if (variable.value.isDefined) => variable.value.getNonNullable
         case VariableDefined | VariableSet if (variable.value.isDefined) => {
-          if (maybeState.isDefined && maybeState.get.isInstanceOf[PState])
-            maybeState.get.asInstanceOf[PState].markVariableRead(vrd)
+          if (pstate.isInstanceOf[PState])
+            pstate.asInstanceOf[PState].markVariableRead(vrd)
 
           variable.setState(VariableRead)
           variable.value.getNonNullable
@@ -362,10 +396,8 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
         // forceExpressionEvaluations function after which all variables should
         // have a defined value
         case VariableDefined if (!variable.value.isDefined && variable.defaultValueExpr.isDefined) => {
-          Assert.invariant(maybeState.isDefined)
-          variable.setState(VariableInProcess)
-          val state = maybeState.get
-          val res = DataValue.unsafeFromAnyRef(variable.defaultValueExpr.get.evaluate(state))
+          variable.setState(VariableBeingDefined)
+          val res = DataValue.unsafeFromAnyRef(variable.defaultValueExpr.get.evaluate(pstate))
 
           // Need to update the variable's value with the result of the
           // expression
@@ -374,7 +406,8 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
 
           res
         }
-        case VariableInProcess => throw new VariableCircularDefinition(varQName, vrd)
+        case VariableBeingDefined => throw new VariableCircularDefinition(varQName, vrd)
+        case VariableInProcess => throw new VariableSuspended(varQName, vrd)
         case _ => throw new VariableHasNoValue(varQName, vrd)
       }
     } else
@@ -386,9 +419,9 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
    */
   def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive, referringContext: ThrowsSDE, pstate: ParseOrUnparseState) = {
     val varQName = vrd.globalQName
-    val abuf = vTable.get(varQName)
-    if (abuf.isDefined) {
-      val variable = abuf.get.last
+    val variableInstances = vTable.get(varQName)
+    if (variableInstances.isDefined) {
+      val variable = variableInstances.get.last
       variable.state match {
         case VariableSet => {
           referringContext.SDE("Cannot set variable %s twice. State was: %s. Existing value: %s",
@@ -407,6 +440,21 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
         }
 
         case _ => {
+          vrd.direction match {
+            /**
+             * Due to potential race conditions regarding the setting of
+             * variables via setVariable and default values in cominbation with
+             * suspesions during unparsing, we only allow the use of either
+             * setVariable statements or a default value when unparsing a
+             * variable.
+             */
+            case VariableDirection.UnparseOnly | VariableDirection.Both if (vrd.maybeDefaultValueExpr.isDefined && variableInstances.get.size > 1) => {
+              // Variable has an unparse direction, a default value, and a
+              // newVariableInstance
+              pstate.SDE("Variable %s has an unparse direction and a default value, setting the variable may cause race conditions when combined with a forward referencing expression.", varQName)
+            }
+            case _ => // Do nothing
+          }
           variable.setValue(VariableUtils.convert(newValue.getAnyRef.toString, variable.rd, referringContext))
           variable.setState(VariableSet)
         }
@@ -415,30 +463,38 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
   }
 
   /**
-   * Creates a new instance of a variable
+   * Creates a new instance of a variable with default value
    */
-  def newVariableInstance(vrd: VariableRuntimeData, state: ParseOrUnparseState) = {
+  def newVariableInstance(vrd: VariableRuntimeData, defaultValue: DataValuePrimitive) = {
     val varQName = vrd.globalQName
-    val abuf = vTable.get(varQName)
-    Assert.invariant(abuf.isDefined)
+    val variableInstances = vTable.get(varQName)
+    Assert.invariant(variableInstances.isDefined)
 
-    if (vrd.maybeDefaultValueExpr.isDefined) {
-      val defaultValue = DataValue.unsafeFromAnyRef(vrd.maybeDefaultValueExpr.get.evaluate(state))
-      abuf.get += vrd.createVariableInstance(VariableUtils.convert(defaultValue.getAnyRef.toString, vrd, vrd))
-    } else
-      abuf.get += vrd.createVariableInstance()
+    variableInstances.get += vrd.createVariableInstance(VariableUtils.convert(defaultValue.getAnyRef.toString, vrd, vrd))
+  }
+
+  /**
+   * Creates a new instance of a variable without default value
+   */
+  def newVariableInstance(vrd: VariableRuntimeData) = {
+    val varQName = vrd.globalQName
+    val variableInstances = vTable.get(varQName)
+    Assert.invariant(variableInstances.isDefined)
+
+    val v = vrd.createVariableInstance()
+    variableInstances.get += v
   }
 
   def removeVariableInstance(vrd: VariableRuntimeData): Unit = {
     val varQName = vrd.globalQName
-    val abuf = vTable.get(varQName)
-    Assert.invariant(abuf.isDefined)
-    abuf.get.trimEnd(1)
+    val variableInstances = vTable.get(varQName)
+    Assert.invariant(variableInstances.isDefined && variableInstances.get.nonEmpty)
+    variableInstances.get.trimEnd(1)
   }
 
 
   private lazy val externalVarGlobalQNames: Seq[GlobalQName] =
-    vTable.map { case (_, abuf) if (abuf.last.rd.external) => abuf.last.rd.globalQName }.toSeq
+    vTable.map { case (_, variableInstances) if (variableInstances.last.rd.external) => variableInstances.last.rd.globalQName }.toSeq
 
   /**
    * Assigns an external variable and sets the variables state to VariableSet
@@ -465,11 +521,11 @@ class VariableMap private(vTable: Map[GlobalQName, ArrayBuffer[VariableInstance]
         bindingQName
       }
 
-    val abuf = vTable.get(varQName.toGlobalQName)
-    if (!abuf.isDefined)
+    val variableInstances = vTable.get(varQName.toGlobalQName)
+    if (!variableInstances.isDefined)
       referringContext.schemaDefinitionError("unknown variable %s", varQName)
     else {
-      val variable = abuf.get.last
+      val variable = variableInstances.get.last
       variable.state match {
         case VariableDefined if (!variable.rd.external) => {
           referringContext.SDE("Cannot set variable %s externally. State was: %s. Existing value: %s.",
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
index c187005..14cc553 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
@@ -166,6 +166,11 @@ class NewVariableInstanceStartParser(override val context: VariableRuntimeData)
 
   def parse(start: PState): Unit = {
     start.newVariableInstance(context)
+    if (context.maybeDefaultValueExpr.isDefined) {
+      val v = start.variableMap.find(context.globalQName).get
+      val res = DataValue.unsafeFromAnyRef(context.maybeDefaultValueExpr.get.evaluate(start))
+      v.setDefaultValue(res)
+    }
   }
 }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
index 0a25927..9dbc258 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
@@ -344,7 +344,7 @@ final class PState private (
   }
 
   def newVariableInstance(vrd: VariableRuntimeData): Unit = {
-    variableMap.newVariableInstance(vrd, this)
+    variableMap.newVariableInstance(vrd)
     changedVariablesStack.top += vrd.globalQName
   }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
index b450514..fa21f25 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
@@ -53,7 +53,6 @@ import org.apache.daffodil.processors.TermRuntimeData
 import org.apache.daffodil.processors.UnparseResult
 import org.apache.daffodil.processors.VariableBox
 import org.apache.daffodil.processors.VariableMap
-import org.apache.daffodil.processors.VariableRuntimeData
 import org.apache.daffodil.processors.charset.BitsCharset
 import org.apache.daffodil.processors.charset.BitsCharsetDecoder
 import org.apache.daffodil.processors.charset.BitsCharsetEncoder
@@ -341,14 +340,6 @@ abstract class UState(
 
   def documentElement: DIDocument
 
-  def newVariableInstance(vrd: VariableRuntimeData): Unit = {
-    variableMap.newVariableInstance(vrd, this)
-  }
-
-  def removeVariableInstance(vrd: VariableRuntimeData): Unit = {
-    variableMap.removeVariableInstance(vrd)
-  }
-
   final val releaseUnneededInfoset: Boolean = !areDebugging && tunable.releaseUnneededInfoset
 
   def delimitedParseResult = Nope
@@ -439,7 +430,6 @@ final class UStateForSuspension(
   override def incrementHiddenDef = Assert.usageError("Unparser suspended UStates need not be aware of hidden contexts")
   override def decrementHiddenDef = Assert.usageError("Unparser suspended UStates need not be aware of hidden contexts")
   override def withinHiddenNest = Assert.usageError("Unparser suspended UStates need not be aware of hidden contexts")
-
 }
 
 final class UStateMain private (
@@ -504,7 +494,7 @@ final class UStateMain private (
     val clone = new UStateForSuspension(
       this,
       suspendedDOS,
-      variableBox,
+      variableBox.cloneForSuspension,
       currentInfosetNodeStack.top.get, // only need the to of the stack, not the whole thing
       arrayIndexStack.top, // only need the top of the stack, not the whole thing
       es,
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
index 4c79505..589e658 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
@@ -19,7 +19,9 @@
 <tdml:testSuite xmlns="http://example.com"
   xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns:dfdlx="http://www.ogf.org/dfdl/dfdl-1.0/extensions"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ct="http://w3.ibm.com/xmlns/dfdl/ctInfoset"
+  xmlns:fn="http://www.w3.org/2005/xpath-functions"
   xmlns:ex="http://example.com"
   defaultRoundTrip="true">
   
@@ -437,6 +439,88 @@
       </xs:complexType>
     </xs:element>
 
+    <dfdl:defineVariable name="v1" type="xs:string" defaultValue="v1_global_default" />
+    <dfdl:defineVariable name="v2" type="xs:string" />
+    <xs:element name="nvi14">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:setVariable ref="ex:v1">{ "v1_global_set" }</dfdl:setVariable>
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="nest1">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/">
+                    <dfdl:newVariableInstance ref="ex:v1"
+                      defaultValue="v1_nest1_nvi1_default" />
+                    <dfdl:newVariableInstance ref="ex:v2" />
+                    <dfdl:setVariable ref="ex:v2">{ "v2_nest1_nvi1_set" }</dfdl:setVariable>
+                  </xs:appinfo>
+                </xs:annotation>
+                <xs:element name="nest2">
+                  <xs:complexType>
+                    <xs:sequence>
+                      <xs:annotation>
+                        <xs:appinfo source="http://www.ogf.org/dfdl/">
+                          <dfdl:newVariableInstance ref="ex:v1"
+                            defaultValue="v1_nest2_nvi2_default" />
+                          <dfdl:newVariableInstance ref="ex:v2" />
+                        </xs:appinfo>
+                      </xs:annotation>
+                      <xs:element name="choice">
+                        <xs:complexType>
+                          <xs:choice>
+                            <xs:element name="c1_v2" type="xs:string" dfdl:lengthKind="delimited">
+                              <xs:annotation>
+                                <xs:appinfo source="http://www.ogf.org/dfdl/">
+                                  <dfdl:setVariable ref="ex:v2">{ "v2_c1_set" }</dfdl:setVariable>
+                                  <dfdl:assert><![CDATA[{ xs:string(.) eq "fail" }]]></dfdl:assert>
+                                </xs:appinfo>
+                              </xs:annotation>
+                            </xs:element>
+                            <xs:element name="c2_v2" type="xs:string" dfdl:lengthKind="delimited">
+                              <xs:annotation>
+                                <xs:appinfo source="http://www.ogf.org/dfdl/">
+                                  <dfdl:setVariable ref="ex:v2">{ "v2_c2_set" }</dfdl:setVariable>
+                                  <dfdl:assert><![CDATA[{ xs:string(.) eq "fail" }]]></dfdl:assert>
+                                </xs:appinfo>
+                              </xs:annotation>
+                            </xs:element>
+                            <xs:element name="c3_v2" type="xs:string" dfdl:lengthKind="delimited">
+                              <xs:annotation>
+                                <xs:appinfo source="http://www.ogf.org/dfdl/">
+                                  <dfdl:setVariable ref="ex:v2">{ "v2_c3_set" }</dfdl:setVariable>
+                                  <dfdl:assert><![CDATA[{ xs:string(.) eq "nest2_nvi2_default2" }]]></dfdl:assert>
+                                </xs:appinfo>
+                              </xs:annotation>
+                            </xs:element>
+                          </xs:choice>
+                        </xs:complexType>
+                      </xs:element>
+                      <xs:element name="d" type="xs:string"
+                        dfdl:inputValueCalc="{ $ex:v1 }" />
+                      <xs:element name="e" type="xs:string"
+                        dfdl:inputValueCalc="{ $ex:v2 }" />
+                    </xs:sequence>
+                  </xs:complexType>
+                </xs:element>
+                <xs:element name="f" type="xs:string"
+                  dfdl:inputValueCalc="{ $ex:v1 }">
+                </xs:element>
+                <xs:element name="g" type="xs:string"
+                  dfdl:inputValueCalc="{ $ex:v2 }" />
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+          <xs:element name="h" type="xs:string"
+            dfdl:inputValueCalc="{ $ex:v1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
     <xs:element name="defNonConst">
       <xs:complexType>
         <xs:sequence>
@@ -764,6 +848,20 @@
 
   </tdml:parserTestCase>
 
+  <tdml:parserTestCase name="setAfterDefaultUnparseErr" root="r2"
+    model="v"
+    description="Error to set the value of a variable instance that has a default value when unparsing">
+
+    <tdml:document />
+
+    <tdml:errors>
+      <tdml:error>variable</tdml:error>
+      <tdml:error>after</tdml:error>
+      <tdml:error>v_with_default</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
   <tdml:parserTestCase name="varInstance_01" root="nvi1"
     model="v"
     description="Simple demonstration of newVariableInstance">
@@ -876,29 +974,16 @@
 
   <tdml:parserTestCase name="varInstance_08" root="nvi8"
     model="v"
-    description="Demonstrates that newVariableInstance correctly goes into and out of scope with backtracking">
+    description="Demonstrates that newVariableInstance default values cannot be used in combination with setVariable when unparsing">
 
     <tdml:document><![CDATA[nest2_nvi2_default2]]></tdml:document>
 
-    <tdml:infoset>
-      <tdml:dfdlInfoset>
-        <nvi8 xmlns="http://example.com">
-          <nest1>
-            <nest2>
-              <choice>
-                <c3_s2>nest2_nvi2_default2</c3_s2>
-              </choice>
-              <d>c3_s1_set1</d>
-              <e>nest2_nvi2_default2</e>
-            </nest2>
-            <f>nest1_nvi1_set1</f>
-            <g>nest1_nvi1_default2</g>
-          </nest1>
-          <h>global_set1</h>
-          <i>global_default2</i>
-        </nvi8>
-      </tdml:dfdlInfoset>
-    </tdml:infoset>
+    <tdml:errors>
+      <tdml:error>unparse direction</tdml:error>
+      <tdml:error>default value</tdml:error>
+      <tdml:error>race condition</tdml:error>
+      <tdml:error>forward referencing expression</tdml:error>
+    </tdml:errors>
   </tdml:parserTestCase>
 
   <tdml:parserTestCase name="varInstance_09" root="nvi9"
@@ -952,7 +1037,7 @@
   </tdml:parserTestCase>
 
   <tdml:parserTestCase name="varInstance_12" root="nvi12"
-    model="v"
+    model="v" roundTrip="false"
     description="newVariablesInstance with a non-constant expression as defaultValue">
 
     <tdml:document><![CDATA[7]]></tdml:document>
@@ -970,6 +1055,32 @@
     </tdml:infoset>
   </tdml:parserTestCase>
 
+  <tdml:parserTestCase name="varInstance_14" root="nvi14"
+    model="v"
+    description="Demonstrates that newVariableInstance correctly goes into and out of scope with backtracking">
+
+    <tdml:document><![CDATA[nest2_nvi2_default2]]></tdml:document>
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi14 xmlns="http://example.com">
+          <nest1>
+            <nest2>
+              <choice>
+                <c3_v2>nest2_nvi2_default2</c3_v2>
+              </choice>
+              <d>v1_nest2_nvi2_default</d>
+              <e>v2_c3_set</e>
+            </nest2>
+            <f>v1_nest1_nvi1_default</f>
+            <g>v2_nest1_nvi1_set</g>
+          </nest1>
+          <h>v1_global_set</h>
+        </nvi14>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
   <tdml:parserTestCase name="defineVariable_nonConstantExpression" root="defNonConst"
     model="v"
     description="defineVariable with a non-constant expression as defaultValue">
@@ -2078,4 +2189,375 @@
     </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="remainingDottedAddr" type="xs:string" dfdlx:direction="unparseOnly" />
+    <dfdl:defineVariable name="priorRemainingDottedAddr" type="xs:string" dfdlx:direction="unparseOnly" />
+    <dfdl:defineVariable name="ipAddr" type="xs:string" dfdlx:direction="unparseOnly" />
+
+		 <xs:group name="IPSrcGrp">
+			<xs:sequence>
+				<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+					<dfdl:newVariableInstance ref="ex:ipAddr" defaultValue='{ ex:IPSrc }'/><!-- example 1.2.3.4 -->
+				</xs:appinfo></xs:annotation>
+				<xs:element name="IPSrcString">
+					<xs:complexType>
+						<xs:group ref="ex:IPAddressGroup"/>
+					</xs:complexType>
+				</xs:element>
+			</xs:sequence>
+		</xs:group>
+
+		<xs:group name="IPAddressGroup">
+			<xs:sequence>
+				<xs:sequence>
+					<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+						<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:ipAddr }'/><!-- example 1.2.3.4 -->
+						<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ $ex:priorRemainingDottedAddr }'/><!-- example 1.2.3.4 -->
+					</xs:appinfo></xs:annotation>
+					<xs:element name="Byte1" type="xs:unsignedByte" dfdl:outputValueCalc="{
+						xs:unsignedByte(fn:substring-before($ex:remainingDottedAddr, '.'))
+						}"/>
+					<xs:sequence>
+						<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+							<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:remainingDottedAddr }'/><!-- example 1.2.3.4 -->
+							<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ fn:substring-after($ex:priorRemainingDottedAddr, ".") }'/><!-- example 2.3.4 -->
+						</xs:appinfo></xs:annotation>
+						<xs:element name="Byte2" type="xs:unsignedByte" dfdl:outputValueCalc="{
+						 xs:unsignedByte(fn:substring-before($ex:remainingDottedAddr, '.'))
+						 }"/>
+						<xs:sequence>
+							<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+								<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:remainingDottedAddr }'/><!-- example 2.3.4 -->
+								<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ fn:substring-after($ex:priorRemainingDottedAddr, ".") }'/><!-- example 3.4 -->
+							</xs:appinfo></xs:annotation>
+							<xs:element name="Byte3" type="xs:unsignedByte" dfdl:outputValueCalc="{
+								xs:unsignedByte(fn:substring-before($ex:remainingDottedAddr, '.'))
+								}"/>
+							<xs:sequence>
+								<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+									<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:remainingDottedAddr }'/><!-- example 3.4 -->
+									<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ fn:substring-after($ex:priorRemainingDottedAddr, ".") }'/><!-- example 4 -->
+								</xs:appinfo></xs:annotation>
+								<xs:element name="Byte4" type="xs:unsignedByte" dfdl:outputValueCalc="{
+									xs:unsignedByte($ex:remainingDottedAddr)
+									}"/>
+							</xs:sequence>
+						</xs:sequence>
+					</xs:sequence>
+				</xs:sequence>
+			</xs:sequence>
+		</xs:group>
+
+    <xs:element name="root">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:sequence dfdl:hiddenGroupRef="ex:IPSrcGrp" />
+          <xs:element name="IPSrc" type="xs:string" dfdl:inputValueCalc="{ fn:concat(../ex:IPSrcString/ex:Byte1, '.', ../ex:IPSrcString/ex:Byte2, '.', ../ex:IPSrcString/ex:Byte3, '.', ../ex:IPSrcString/ex:Byte4) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="varDirection_1" root="root"
+    model="variable_direction_schema" description="Section 7 - ">
+    <tdml:document>
+      <tdml:documentPart type="bits">00000001 00000010 00000011 00000100</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <root>
+          <IPSrc>1.2.3.4</IPSrc>
+        </root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:defineSchema name="variable_direction_simple">
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format ref="ex:GeneralFormat"
+      representation="text" textNumberRep="standard" alignment="8" alignmentUnits="bits"
+      lengthUnits="bytes" lengthKind="delimited" />
+
+    <dfdl:defineVariable name="input" type="xs:int" dfdlx:direction="parseOnly" defaultValue="{ 1 }" />
+    <dfdl:defineVariable name="output" type="xs:int" dfdlx:direction="unparseOnly" defaultValue="{ 2 }" />
+
+    <xs:element name="e1" type="xs:int" dfdl:inputValueCalc="{ $ex:input }" />
+    <xs:element name="e2" type="xs:int" dfdl:inputValueCalc="{ $ex:output }" />
+
+    <xs:element name="refSimpleElementWithOvcVar">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element ref="st1" dfdl:outputValueCalc="{ $ex:output }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="st1" type="xs:int" />
+
+    <xs:element name="refSimpleElementWithOvcVar2">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element ref="st1" dfdl:outputValueCalc="{ $ex:input }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="direction_setVar1">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:setVariable ref="ex:output">{ 4 }</dfdl:setVariable>
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="st1" type="xs:int" dfdl:inputValueCalc="{ $ex:output }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="direction_setVar2">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:setVariable ref="ex:output">{ 4 }</dfdl:setVariable>
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="st1" type="xs:int" dfdl:outputValueCalc="{ $ex:output }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="direction_nvi1">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:newVariableInstance ref="ex:output" defaultValue="4" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="st1" type="xs:int" dfdl:inputValueCalc="{ $ex:output }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="direction_nvi2">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:newVariableInstance ref="ex:output" defaultValue="4" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="st1" type="xs:int" dfdl:outputValueCalc="{ $ex:output }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="varDirection_2" root="e1"
+    model="variable_direction_simple" description="Simple test of parseOnly variable" >
+    <tdml:document />
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <e1>1</e1>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="varDirection_3" root="refSimpleElementWithOvcVar"
+    model="variable_direction_simple" description="Simple test of unparseOnly variable" roundTrip="false">
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <refSimpleElementWithOvcVar xmlns="http://example.com">
+          <st1>9</st1>
+        </refSimpleElementWithOvcVar>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="text">2</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase name="varDirection_err1" root="e2"
+    model="variable_direction_simple" description="Attempt to read variable marked unparseOnly during parsing" >
+    <tdml:document />
+    <tdml:errors>
+      <tdml:error>Attempting to read variable</tdml:error>
+      <tdml:error>marked as unparseOnly</tdml:error>
+      <tdml:error>during parsing</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="varDirection_err2" root="refSimpleElementWithOvcVar2"
+    model="variable_direction_simple" description="Attempt to read a variable marked parseOnly during unparsing" roundTrip="false">
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <refSimpleElementWithOvcVar2 xmlns="http://example.com">
+          <st1>9</st1>
+        </refSimpleElementWithOvcVar2>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="text">1</tdml:documentPart>
+    </tdml:document>
+
+    <tdml:errors>
+      <tdml:error>Attempting to read variable</tdml:error>
+      <tdml:error>marked as parseOnly</tdml:error>
+      <tdml:error>during unparsing</tdml:error>
+    </tdml:errors>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase name="varDirection_setVar1" root="direction_setVar1"
+    model="variable_direction_simple" description="Attempt to set a variable marked unparseOnly during parsing. SetVar should be ignored" roundTrip="false">
+    <tdml:document />
+    <tdml:errors>
+      <tdml:error>Attempting to read variable</tdml:error>
+      <tdml:error>marked as unparseOnly</tdml:error>
+      <tdml:error>during parsing</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="varDirection_setVar2" root="direction_setVar2"
+    model="variable_direction_simple" description="Attempt to set a variable marked unparseOnly during parsing" roundTrip="false">
+    <tdml:document>
+      <tdml:documentPart type="text">4</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <direction_setVar2 xmlns="http://example.com">
+          <st1>2</st1>
+        </direction_setVar2>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase name="varDirection_nvi1" root="direction_nvi1"
+    model="variable_direction_simple" description="Attempt to set a variable marked unparseOnly during parsing" roundTrip="false">
+    <tdml:document />
+    <tdml:errors>
+      <tdml:error>Attempting to read variable</tdml:error>
+      <tdml:error>marked as unparseOnly</tdml:error>
+      <tdml:error>during parsing</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="varDirection_nvi2" root="direction_nvi2"
+    model="variable_direction_simple" description="Attempt to set a variable marked unparseOnly during parsing" roundTrip="false">
+    <tdml:document>
+      <tdml:documentPart type="text">4</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <direction_nvi2 xmlns="http://example.com">
+          <st1>2</st1>
+        </direction_nvi2>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:unparserTestCase>
+
+  <tdml:defineSchema name="variable_suspension_scope">
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format ref="ex:GeneralFormat"
+      representation="text" lengthKind="delimited"/>
+
+    <dfdl:defineVariable name="remainingDottedAddr" type="xs:string" dfdlx:direction="unparseOnly" />
+    <dfdl:defineVariable name="priorRemainingDottedAddr" type="xs:string" dfdlx:direction="unparseOnly" />
+    <dfdl:defineVariable name="ipAddr" type="xs:string" dfdlx:direction="unparseOnly" />
+
+		 <xs:group name="IPSrcGrp">
+			<xs:sequence>
+				<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+					<dfdl:newVariableInstance ref="ex:ipAddr" defaultValue='{ ex:IPSrc }'/><!-- example 1.2.3.4 -->
+				</xs:appinfo></xs:annotation>
+				<xs:element name="IPSrcString">
+					<xs:complexType>
+						<xs:group ref="ex:IPAddressGroup"/>
+					</xs:complexType>
+				</xs:element>
+			</xs:sequence>
+		</xs:group>
+
+		<xs:group name="IPAddressGroup">
+			<xs:sequence dfdl:separator=",">
+				<xs:sequence dfdl:separator=",">
+					<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+						<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:ipAddr }'/><!-- example 1.2.3.4 -->
+						<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ $ex:priorRemainingDottedAddr }'/><!-- example 1.2.3.4 -->
+					</xs:appinfo></xs:annotation>
+					<xs:element name="Byte1" type="xs:unsignedByte" dfdl:outputValueCalc="{
+						xs:unsignedByte(fn:substring-before($ex:remainingDottedAddr, '.'))
+						}"/>
+					<xs:sequence dfdl:separator=",">
+						<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+							<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:remainingDottedAddr }'/><!-- example 1.2.3.4 -->
+							<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ fn:substring-after($ex:priorRemainingDottedAddr, ".") }'/><!-- example 2.3.4 -->
+						</xs:appinfo></xs:annotation>
+						<xs:element name="Byte2" type="xs:unsignedByte" dfdl:outputValueCalc="{
+						 xs:unsignedByte(fn:substring-before($ex:remainingDottedAddr, '.'))
+						 }"/>
+						<xs:sequence dfdl:separator=",">
+							<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+								<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:remainingDottedAddr }'/><!-- example 2.3.4 -->
+								<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ fn:substring-after($ex:priorRemainingDottedAddr, ".") }'/><!-- example 3.4 -->
+							</xs:appinfo></xs:annotation>
+							<xs:element name="Byte3" type="xs:unsignedByte" dfdl:outputValueCalc="{
+								xs:unsignedByte(fn:substring-before($ex:remainingDottedAddr, '.'))
+								}"/>
+							<xs:sequence dfdl:separator=",">
+								<xs:annotation><xs:appinfo source="http://www.ogf.org/dfdl/">
+									<dfdl:newVariableInstance ref="ex:priorRemainingDottedAddr" defaultValue='{ $ex:remainingDottedAddr }'/><!-- example 3.4 -->
+									<dfdl:newVariableInstance ref="ex:remainingDottedAddr" defaultValue='{ fn:substring-after($ex:priorRemainingDottedAddr, ".") }'/><!-- example 4 -->
+								</xs:appinfo></xs:annotation>
+								<xs:element name="Byte4" type="xs:unsignedByte" dfdl:outputValueCalc="{
+									xs:unsignedByte($ex:remainingDottedAddr)
+									}"/>
+              </xs:sequence>
+              <xs:element name="checkPrior3" type="xs:string" dfdl:outputValueCalc="{ $ex:priorRemainingDottedAddr }" />
+              <xs:element name="checkRemaining3" type="xs:string" dfdl:outputValueCalc="{ $ex:remainingDottedAddr }" />
+						</xs:sequence>
+            <xs:element name="checkPrior2" type="xs:string" dfdl:outputValueCalc="{ $ex:priorRemainingDottedAddr }" />
+            <xs:element name="checkRemaining2" type="xs:string" dfdl:outputValueCalc="{ $ex:remainingDottedAddr }" />
+					</xs:sequence>
+          <xs:element name="checkPrior1" type="xs:string" dfdl:outputValueCalc="{ $ex:priorRemainingDottedAddr }" />
+          <xs:element name="checkRemaining1" type="xs:string" dfdl:outputValueCalc="{ $ex:remainingDottedAddr }" />
+				</xs:sequence>
+			</xs:sequence>
+		</xs:group>
+
+    <xs:element name="root">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:sequence dfdl:hiddenGroupRef="ex:IPSrcGrp" />
+          <xs:element name="IPSrc" type="xs:string" dfdl:inputValueCalc="{ fn:concat(../ex:IPSrcString/ex:Byte1, '.', ../ex:IPSrcString/ex:Byte2, '.', ../ex:IPSrcString/ex:Byte3, '.', ../ex:IPSrcString/ex:Byte4) }" />
+
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+  </tdml:defineSchema>
+
+  <tdml:unparserTestCase name="varInstance_13" root="root"
+    model="variable_suspension_scope" roundTrip="false"
+    description="Demonstrate suspensions work correctly as NVI goes out of scope">
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <root>
+          <IPSrc>1.2.3.4</IPSrc>
+        </root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="text">1,2,3,4,2.3.4,3.4,1.2.3.4,2.3.4,1.2.3.4,1.2.3.4</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
 </tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
index 3d448df..cc6716d 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
@@ -60,6 +60,18 @@ class TestVariables {
   @Test def test_varInstance_10(): Unit = { runner.runOneTest("varInstance_10") }
   @Test def test_varInstance_11(): Unit = { runner.runOneTest("varInstance_11") }
   @Test def test_varInstance_12(): Unit = { runner.runOneTest("varInstance_12") }
+  @Test def test_varInstance_13(): Unit = { runner.runOneTest("varInstance_13") }
+  @Test def test_varInstance_14(): Unit = { runner.runOneTest("varInstance_14") }
+
+  @Test def test_varDirection_1(): Unit = { runner.runOneTest("varDirection_1") }
+  @Test def test_varDirection_2(): Unit = { runner.runOneTest("varDirection_2") }
+  @Test def test_varDirection_3(): Unit = { runner.runOneTest("varDirection_3") }
+  @Test def test_varDirection_err1(): Unit = { runner.runOneTest("varDirection_err1") }
+  @Test def test_varDirection_err2(): Unit = { runner.runOneTest("varDirection_err2") }
+  @Test def test_varDirection_setVar1(): Unit = { runner.runOneTest("varDirection_setVar1") }
+  @Test def test_varDirection_setVar2(): Unit = { runner.runOneTest("varDirection_setVar2") }
+  @Test def test_varDirection_nvi1(): Unit = { runner.runOneTest("varDirection_nvi1") }
+  @Test def test_varDirection_nvi2(): Unit = { runner.runOneTest("varDirection_nvi2") }
 
   @Test def test_defineVariable_nonConstantExpression(): Unit = { runner.runOneTest("defineVariable_nonConstantExpression") }
   @Test def test_defineVariable_nonConstantExpression_unp(): Unit = { runner.runOneTest("defineVariable_nonConstantExpression_unp") }