This is an automated email from the ASF dual-hosted git repository.
slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git
The following commit(s) were added to refs/heads/master by this push:
new ebd1af4 Free infoset element during unparse
ebd1af4 is described below
commit ebd1af445b9c9892481d50bdd2f094eec4f503db
Author: Steve Lawrence <[email protected]>
AuthorDate: Wed Oct 14 16:29:11 2020 -0400
Free infoset element during unparse
- This refactors how we figure out when DINodes are final during
unparsing, mainly for arrays. Previously, arrays were marked final
when their parent became final, but that meant arrays could
potentially stick around for a long time. Instead, we set arrays as
final when a new element is added after it, or if it's the last thing
in a complex type. In both cases, we know no more elements will be
added and the array is final
- After marking DINodes as final we free them if they are not used in an
expression to reduce memory requirements
- Changes dfdlx:outputTypeCalcNextSibling function to mark possible next
siblings and itself as used in an expression, since both are needed in
the function implementation so we cannot free those elements.
- Add new releaseUnneededInfoset tunable which allows one to disable
this freeing of infoset elements, for both parse and unparse. This is
helpful when running unit tests that do manual inspect of the infoset
after it is complete to ensure correctness. We can't have these
elements being freed. Refactor existing variable names to match this
tunable
- Adds areDebugging logic to UState, similar to PState, so we can
disable freeing infoset nodes while debugging.
- Add state to the infoset so we can track which elements would have
been free when freeing is disabled. Modified/refactored
ScalaXMLInfosetOuputter to optionally add an attribute with this
state. And add tests that enable this attribute so that we can easily
test that elements are being released as expected.
DAFFODIL-1272
---
.../org/apache/daffodil/dpath/Expression.scala | 12 +
.../dpath/TestDFDLExpressionEvaluation.scala | 4 +-
.../org/apache/daffodil/infoset/TestInfoset.scala | 13 +-
.../apache/daffodil/infoset/TestInfosetFree.scala | 282 +++++++++++++++++++++
.../resources/org/apache/daffodil/xsd/dafext.xsd | 10 +
.../processors/unparsers/ElementUnparser.scala | 145 ++++++++---
.../daffodil/debugger/InteractiveDebugger.scala | 2 +-
.../apache/daffodil/dsom/CompiledExpression1.scala | 2 +-
.../org/apache/daffodil/infoset/InfosetImpl.scala | 63 +++--
.../apache/daffodil/infoset/InfosetWalker.scala | 22 +-
.../infoset/ScalaXMLInfosetOutputter.scala | 96 ++++---
.../apache/daffodil/processors/DataProcessor.scala | 3 +-
.../daffodil/processors/parsers/PState.scala | 2 +-
.../daffodil/processors/unparsers/UState.scala | 36 ++-
14 files changed, 560 insertions(+), 132 deletions(-)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
index c2f8d03..f4fde0e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
@@ -1730,6 +1730,18 @@ case class FunctionCallExpression(functionQNameString:
String, expressions: List
ans
}
}
+
+ // We need to mark all possible following compileInfos as possibly used
+ // in an expression since the DFDLXOutputTypeCalcNextSibling expression
+ // might need them. We also need to mark this compileInfo as used in an
+ // expression since the evaluate method of DFDLXTypeCalcNextSibilng
+ // uses this element to find the next sibling
+ val dpeci = erd.dpathCompileInfo.asInstanceOf[DPathElementCompileInfo]
+ followingERDs.foreach { erd =>
+
dpeci.indicateReferencedByExpression(Seq(erd.dpathElementCompileInfo))
+ }
+ dpeci.indicateReferencedByExpression(Seq(dpeci))
+
FNZeroArgExpr(functionQNameString, functionQName,
dstType, NodeInfo.AnyAtomic, DFDLXOutputTypeCalcNextSibling(_, _))
}
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/dpath/TestDFDLExpressionEvaluation.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/dpath/TestDFDLExpressionEvaluation.scala
index bf25f4c..ed88479 100644
---
a/daffodil-core/src/test/scala/org/apache/daffodil/dpath/TestDFDLExpressionEvaluation.scala
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/dpath/TestDFDLExpressionEvaluation.scala
@@ -36,7 +36,9 @@ import org.apache.daffodil.infoset.InfosetDocument
class TestDFDLExpressionEvaluation extends Parsers {
def testExpr(testSchema: scala.xml.Elem, infosetAsXML: scala.xml.Elem, expr:
String)(body: Any => Unit): Unit = {
- val schemaCompiler =
Compiler().withTunable("allowExternalPathExpressions", "true")
+ val schemaCompiler = Compiler()
+ .withTunable("allowExternalPathExpressions", "true")
+ .withTunable("releaseUnneededInfoset", "false")
val pf =
schemaCompiler.compileNode(testSchema).asInstanceOf[ProcessorFactory]
val sset = pf.sset
if (pf.isError) fail("pf compile errors")
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfoset.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfoset.scala
index 249df7d..98c2901 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfoset.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfoset.scala
@@ -74,12 +74,17 @@ object TestInfoset {
}
/**
- * Returns the root element of the infoset, along with
- * the schema compiler Root object for examining schema-compiler
- * computations for unit testing them.
+ * Returns the root element of the infoset, along with the schema compiler
+ * Root object for examining schema-compiler computations for unit testing
+ * them. Because this assumes tests will be inspecting the internal infoset
+ * for correctness, it sets the releaseUnusedInfoset tunable to false so that
+ * the infoset elements are not freed
*/
def testInfoset(testSchema: scala.xml.Elem, infosetAsXML: scala.xml.Elem):
(DIElement, Root, DaffodilTunables) = {
- val schemaCompiler =
Compiler().withTunable("allowExternalPathExpressions", "true")
+ val schemaCompiler =
+ Compiler()
+ .withTunable("allowExternalPathExpressions", "true")
+ .withTunable("releaseUnneededInfoset", "false")
val pf =
schemaCompiler.compileNode(testSchema).asInstanceOf[ProcessorFactory]
if (pf.isError) {
val msgs = pf.getDiagnostics.map { _.getMessage() }.mkString("\n")
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfosetFree.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfosetFree.scala
new file mode 100644
index 0000000..e323cf7
--- /dev/null
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/infoset/TestInfosetFree.scala
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.infoset
+
+import java.nio.channels.Channels
+
+import org.apache.commons.io.output.NullOutputStream
+
+import org.junit.Assert._
+import org.junit.Test
+
+import org.apache.daffodil.compiler.Compiler
+import org.apache.daffodil.io.InputSourceDataInputStream
+import org.apache.daffodil.processors.parsers.PState
+import org.apache.daffodil.processors.unparsers.UStateMain
+import org.apache.daffodil.util.SchemaUtils
+
+object TestInfosetFree {
+
+ /**
+ * Compiles an infoset with infoset releasing disabled. By disabling the
+ * releasing, the Scala infoset is marked with attributes signifying which
+ * elements *would* have been freed if we didn't disable it. This allows for
+ * a reliable way to validate which elements were freed.
+ *
+ * This first parses and unparses data, without freeing any infoset
+ * nodes--only marking as described above. It then walks both infosets,
+ * configured to add information to the infoset about which elements were
+ * freed. We validate that both the parse and unparse results are the
+ * same--any infoset elements freed during a parse should also be freed
+ * during unparse. We then return the infoset for the test to compare against
+ * the expected value.
+ */
+ def test(
+ schema: scala.xml.Elem,
+ bytes: Array[Byte]): scala.xml.Node = {
+
+ val compiler = Compiler()
+ .withTunable("releaseUnneededInfoset", "false")
+
+ val pf = compiler.compileNode(schema)
+ if (pf.isError) {
+ val msgs = pf.getDiagnostics.map { _.getMessage() }.mkString("\n")
+ fail("pf compile errors: " + msgs)
+ }
+ pf.sset.root.erd.preSerialization // force evaluation of all compile-time
constructs
+ val dp = pf.onPath("/")
+ if (dp.isError) {
+ val msgs = dp.getDiagnostics.map { _.getMessage() }.mkString("\n")
+ fail("dp compile errors: " + msgs)
+ }
+
+ // Parse the data, note that we do not set showFreedInfo here because
+ // DINodes aren't freed until *after* the infoset walker walks them. So the
+ // maybeFreed state hasn't been set yet when the infoset outputter gets the
+ // events. We must walk the infoset again after the parse is complete
+ val parseInput = InputSourceDataInputStream(bytes)
+ val parseOutputter = new ScalaXMLInfosetOutputter()
+ val parseResult = dp.parse(parseInput, parseOutputter)
+ if (parseResult.isError) {
+ val msgs = parseResult.getDiagnostics.map { _.getMessage()
}.mkString("\n")
+ fail("parse errors: " + msgs)
+ }
+
+ val unparseInputter = new ScalaXMLInfosetInputter(parseOutputter.getResult)
+ val unparseOutput =
Channels.newChannel(NullOutputStream.NULL_OUTPUT_STREAM)
+ val unparseResult = dp.unparse(unparseInputter, unparseOutput)
+ if (unparseResult.isError) {
+ val msgs = unparseResult.getDiagnostics.map { _.getMessage()
}.mkString("\n")
+ fail("unparse errors: " + msgs)
+ }
+
+ // now walk the parse and unparse infosets and convert them to Scala XML
+ // with the showFreedInfoset set
+
+ def docToXML(doc: DIDocument): scala.xml.Node = {
+ val detailedOutputter = new ScalaXMLInfosetOutputter(
+ showFormatInfo = false,
+ showFreedInfo = true)
+
+ val infosetWalker = InfosetWalker(
+ doc,
+ detailedOutputter,
+ walkHidden = true, // let's ensure any hidden elements are free
+ ignoreBlocks = true, // there should be no blocks, but ignore them
just to be sure
+ releaseUnneededInfoset = false) // do not free the infoset
+ infosetWalker.walk(lastWalk = true)
+
+ detailedOutputter.getResult()
+ }
+
+ val parseDoc =
parseResult.resultState.asInstanceOf[PState].infoset.asInstanceOf[DIDocument]
+ val unparseDoc =
unparseResult.resultState.asInstanceOf[UStateMain].documentElement
+
+ val parseXML = docToXML(parseDoc)
+ val unparseXML = docToXML(unparseDoc)
+
+ if (parseXML.toString != unparseXML.toString) {
+ fail("parse and unparse XML did not match, infoset not freed the same")
+ }
+
+ parseXML
+ }
+}
+
+class TestInfosetFree {
+
+ @Test def testInfosetFree1(): Unit = {
+ val testSchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />,
+ <dfdl:format ref="tns:GeneralFormat" />,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="e" type="xs:int" maxOccurs="unbounded"
+ dfdl:length="1" dfdl:lengthKind="explicit" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>)
+
+ val actualXML = TestInfosetFree.test(testSchema, "123".getBytes)
+
+ // all elements freed, including array elements and the array itself
+ val expectedXML =
+ <root freed="self" xmlns="http://example.com">
+ <e freed="self+array">1</e>
+ <e freed="self+array">2</e>
+ <e freed="self+array">3</e>
+ </root>
+
+ assertEquals(scala.xml.Utility.trim(expectedXML).toString,
actualXML.toString)
+ }
+
+ @Test def testInfosetFree2(): Unit = {
+ val testSchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />,
+ <dfdl:format ref="tns:GeneralFormat" />,
+ <xs:group name="hidden">
+ <xs:sequence>
+ <xs:element name="e" type="xs:int" dfdl:length="1"
dfdl:lengthKind="explicit"
+ dfdl:outputValueCalc="{ 1 }" />
+ </xs:sequence>
+ </xs:group>
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:sequence dfdl:hiddenGroupRef="tns:hidden" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>)
+
+ val actualXML = TestInfosetFree.test(testSchema, "1".getBytes)
+
+ // all elements freed, including hidden elements
+ val expectedXML =
+ <root freed="self" xmlns="http://example.com">
+ <e freed="self">1</e>
+ </root>
+
+ assertEquals(scala.xml.Utility.trim(expectedXML).toString,
actualXML.toString)
+ }
+
+ @Test def testInfosetFree3(): Unit = {
+ val testSchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />,
+ <dfdl:format ref="tns:GeneralFormat" />,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="fieldLen" type="xs:int"
dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:element name="field" type="xs:int" maxOccurs="unbounded"
+ dfdl:lengthKind="explicit" dfdl:length="{ ../tns:fieldLen }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>)
+
+ val actualXML = TestInfosetFree.test(testSchema, "1123".getBytes)
+
+ // all elements freed, execpted for fieldLen because it is used in an
expression
+ val expectedXML =
+ <root freed="self" xmlns="http://example.com">
+ <fieldLen>1</fieldLen>
+ <field freed="self+array">1</field>
+ <field freed="self+array">2</field>
+ <field freed="self+array">3</field>
+ </root>
+
+ assertEquals(scala.xml.Utility.trim(expectedXML).toString,
actualXML.toString)
+ }
+
+ @Test def testInfosetFree4(): Unit = {
+ val testSchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />,
+ <dfdl:format ref="tns:GeneralFormat" />,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="fieldCount" type="xs:int"
dfdl:lengthKind="explicit" dfdl:length="1"
+ dfdl:outputValueCalc="{ fn:count(../tns:field) }" />
+ <xs:element name="field" type="xs:int" maxOccurs="unbounded"
+ dfdl:occursCountKind="expression" dfdl:occursCount="{
../tns:fieldCount }"
+ dfdl:lengthKind="explicit" dfdl:length="1" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>)
+
+ val actualXML = TestInfosetFree.test(testSchema, "3123".getBytes)
+
+ // Most elements are not freed. Both fieldCount and field are used in an
+ // expression. Even though only the count of the field array is needed, and
+ // we could theoretically free the field elements but not the array, they
+ // are not currently freed. Our usedInAnExpression logic is not yet
+ // sophisticated enough to differentiate between how elements are used in
+ // an expression and how that affect releasability
+ val expectedXML =
+ <root freed="self" xmlns="http://example.com">
+ <fieldCount>3</fieldCount>
+ <field>1</field>
+ <field>2</field>
+ <field>3</field>
+ </root>
+
+ assertEquals(scala.xml.Utility.trim(expectedXML).toString,
actualXML.toString)
+ }
+
+ @Test def testInfosetFree5(): Unit = {
+ val testSchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />,
+ <dfdl:format ref="tns:GeneralFormat" />,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="fieldCount" type="xs:int"
dfdl:lengthKind="explicit" dfdl:length="1"
+ dfdl:outputValueCalc="{ fn:count(../tns:fieldLen) }" />
+ <xs:element name="fieldLen" type="xs:int" maxOccurs="unbounded"
+ dfdl:occursCountKind="expression" dfdl:occursCount="{
../tns:fieldCount }"
+ dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:element name="field" type="xs:int" maxOccurs="unbounded"
+ dfdl:occursCountKind="expression" dfdl:occursCount="{
../tns:fieldCount }"
+ dfdl:lengthKind="explicit" dfdl:length="{
../tns:fieldLen[dfdl:occursIndex()] }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>)
+
+ val actualXML = TestInfosetFree.test(testSchema, "3123122333".getBytes)
+
+ // fieldCount and fieldLen are not freed, both are used in expressions.
+ // Individual field's are not use in expressions, so we can free them. Note
+ // that fieldLen does not have an outputValueCalc, which in practice one
+ // might use so that the fieldLen values match the field lengths on
+ // unparse. This shows that if one were to exclude some outputValueCalc
+ // elements, more elements can be freed and streamabilty can be improved.
+ val expectedXML =
+ <root freed="self" xmlns="http://example.com">
+ <fieldCount>3</fieldCount>
+ <fieldLen>1</fieldLen>
+ <fieldLen>2</fieldLen>
+ <fieldLen>3</fieldLen>
+ <field freed="self+array">1</field>
+ <field freed="self+array">22</field>
+ <field freed="self+array">333</field>
+ </root>
+
+ assertEquals(scala.xml.Utility.trim(expectedXML).toString,
actualXML.toString)
+ }
+
+}
diff --git
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
index bbfd02b..f7540c7 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
@@ -315,6 +315,16 @@
</xs:documentation>
</xs:annotation>
</xs:element>
+ <xs:element name="releaseUnneededInfoset" type="xs:boolean"
default="true" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>
+ Daffodil will periodically release internal infoset elements
that it determines
+ are no longer needed, thus freeing memory. Setting this value to
false will
+ prevent this from taking place. This should usually only be used
while debugging
+ or with very specific tests.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
<xs:element name="requireBitOrderProperty" type="xs:boolean"
default="false" minOccurs="0">
<xs:annotation>
<xs:documentation>
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 bf23800..e224c10 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
@@ -406,7 +406,8 @@ sealed trait RegularElementUnparserStartEndStrategy
Assert.invariant(state.currentInfosetNode.asSimple.erd eq erd)
()
} else {
- val elem =
+ // get the new DIElem to add to the infoset
+ val newElem =
if (!state.withinHiddenNest) {
// Elements in a hidden context are not in the infoset, so we will
never get an event
// for them. Only try to consume start events for non-hidden elements
@@ -417,39 +418,54 @@ sealed trait RegularElementUnparserStartEndStrategy
UnparseError(Nope, One(state.currentLocation), "Expected element
start event for %s, but received %s.",
erd.namedQName.toExtendedSyntax, event)
}
- val res = event.info.element
- val mCurNode = state.currentInfosetNodeMaybe
- if (mCurNode.isDefined) {
- val c = mCurNode.get.asComplex
- Assert.invariant(!c.isFinal)
- if (c.maybeIsNilled == MaybeBoolean.True) {
- // cannot add content to a nilled complex element
- UnparseError(One(erd.schemaFileLocation), Nope, "Nilled complex
element %s has content from %s",
- c.erd.namedQName.toExtendedSyntax,
- res.erd.namedQName.toExtendedSyntax)
- }
- c.addChild(res, state.tunable)
- } else {
- val doc = state.documentElement
- doc.addChild(res, state.tunable) // DIDocument, which is never a
current node, must have the child added
- doc.isFinal = true // that's the only child.
- }
- res
+ event.info.element
} else {
Assert.invariant(state.withinHiddenNest)
// Since we never get events for elements in hidden contexts, their
infoset elements
// will have never been created. This means we need to manually
create them
- val e = if (erd.isComplexType) new DIComplex(erd) else new
DISimple(erd)
- e.setHidden()
- state.currentInfosetNode.asComplex.addChild(e, state.tunable)
- e
+ val hiddenElem = if (erd.isComplexType) new DIComplex(erd) else new
DISimple(erd)
+ hiddenElem.setHidden()
+ hiddenElem
+ }
+
+ // now add this new elem to the infoset
+ val parentNodeMaybe = state.currentInfosetNodeMaybe
+ if (parentNodeMaybe.isDefined) {
+ val parentComplex = parentNodeMaybe.get.asComplex
+ Assert.invariant(!parentComplex.isFinal)
+ if (parentComplex.maybeIsNilled == MaybeBoolean.True) {
+ // cannot add content to a nilled complex element
+ UnparseError(One(erd.schemaFileLocation), Nope, "Nilled complex
element %s has content from %s",
+ parentComplex.erd.namedQName.toExtendedSyntax,
+ newElem.erd.namedQName.toExtendedSyntax)
}
+ // We are about to add a child to this complex element. Before we do
+ // that, if the last child added to this complex is a DIArray, and this
+ // new child isn't part of that array, that implies that the DIArray
+ // will have no more children added and should be marked as final, and
+ // we can attempt to free that array.
+ val lastChildMaybe = parentComplex.maybeLastChild
+ if (lastChildMaybe.isDefined) {
+ val lastChild = lastChildMaybe.get
+ if (lastChild.isArray && (lastChild.erd ne newElem.erd)) {
+ lastChild.isFinal = true
+ parentComplex.freeChildIfNoLongerNeeded(parentComplex.numChildren
- 1, state.releaseUnneededInfoset)
+ }
+ }
+
+ parentComplex.addChild(newElem, state.tunable)
+ } else {
+ // We do not yet have an infoset element (this new element is the
+ // root), so add the infoset node to the DIDocument
+ val doc = state.documentElement
+ doc.addChild(newElem, state.tunable)
+ }
+
// When the infoset events are being advanced, the
currentInfosetNodeStack
// is pushing and popping to match the events. This provides the proper
// context for evaluation of expressions.
- val e = One(elem)
- state.currentInfosetNodeStack.push(e)
+ state.currentInfosetNodeStack.push(One(newElem))
}
}
@@ -475,10 +491,40 @@ sealed trait RegularElementUnparserStartEndStrategy
erd.namedQName.toExtendedSyntax, event)
}
}
- val cur = state.currentInfosetNode
- if (cur.isComplex)
- cur.isFinal = true
- state.currentInfosetNodeStack.pop
+
+ val cur = state.currentInfosetNodeStack.pop.get
+
+ if (cur.isComplex) {
+ // We are ending a complex element. If the last child of this complex
+ // is a DIArray, that implies that the array will have no more children
+ // and should be marked as isFinal. Normally this happens when we add a
+ // new sibling after an array in unparseBegin, but in this case there
+ // is no sibling following the array, so it must be set here.
+ val lastChild = cur.maybeLastChild
+ if (lastChild.isDefined && lastChild.get.isArray) {
+ lastChild.get.isFinal = true
+ cur.freeChildIfNoLongerNeeded(cur.numChildren - 1,
state.releaseUnneededInfoset)
+ }
+ }
+
+ // cur is finished, mark it as final and free if possible. Note that we
+ // need the container and not the parent of the current element to free
+ // it. This way if this element is in an array, we free this element
+ // from the array
+ cur.isFinal = true
+ val curContainer =
+ if (cur.erd.isArray) cur.diParent.maybeLastChild.get
+ else cur.diParent
+ curContainer.freeChildIfNoLongerNeeded(curContainer.numChildren - 1,
state.releaseUnneededInfoset)
+
+ if (state.currentInfosetNodeStack.isEmpty) {
+ // If there is nothing else on the infoset stack after popping off the
+ // current infoset node, that means we have finished the root element,
+ // so mark the DIDocument as final
+ val doc = state.documentElement
+ Assert.invariant(!doc.isFinal)
+ doc.isFinal = true
+ }
move(state)
@@ -499,7 +545,7 @@ trait OVCStartEndStrategy
* For OVC, the behavior w.r.t. consuming infoset events is different.
*/
protected final override def unparseBegin(state: UState): Unit = {
- val elem =
+ val ovcElem =
if (!state.withinHiddenNest) {
// outputValueCalc elements are optional in the infoset. If the next
event
// is for this OVC element, then consume the start/end events.
@@ -515,7 +561,6 @@ trait OVCStartEndStrategy
Assert.invariant(endEv.isEnd && endEv.erd == erd)
val e = new DISimple(erd)
- state.currentInfosetNode.asComplex.addChild(e, state.tunable)
// Remove any state that was set by what created this event. Later
// code asserts that OVC elements do not have a value
e.resetValue
@@ -523,27 +568,53 @@ trait OVCStartEndStrategy
} else {
// Event was optional and didn't exist, create a new InfosetElement
and add it
val e = new DISimple(erd)
- state.currentInfosetNode.asComplex.addChild(e, state.tunable)
e
}
} else {
// Event was hidden and will never exist, create a new InfosetElement
and add it
val e = new DISimple(erd)
e.setHidden()
- state.currentInfosetNode.asComplex.addChild(e, state.tunable)
e
}
- val e = One(elem)
- state.currentInfosetNodeStack.push(e)
+ // We are about to add a new OVC child to this complex element. Before we
+ // do that, if the last child added to this complex is a DIArray, that
+ // implies that the DIArray will have no more children added and should be
+ // marked as final, and we can attempt to free that array.
+ val parentNode = state.currentInfosetNode
+ val parentComplex = parentNode.asComplex
+ val lastChildMaybe = parentComplex.maybeLastChild
+ if (lastChildMaybe.isDefined) {
+ val lastChild = lastChildMaybe.get
+ if (lastChild.isArray) {
+ lastChild.isFinal = true
+ parentComplex.freeChildIfNoLongerNeeded(parentComplex.numChildren - 1,
state.releaseUnneededInfoset)
+ }
+ }
+
+ parentComplex.addChild(ovcElem, state.tunable)
+ state.currentInfosetNodeStack.push(One(ovcElem))
}
protected final override def unparseEnd(state: UState): Unit = {
- state.currentInfosetNodeStack.pop
-
// if an OVC element existed, the start AND end events were consumed in
// unparseBegin. No need to advance the cursor here.
+ // ovcElem is finished, free it if possible. OVC elements are not allowed
in
+ // arrays, so we can directly get the diParent to get the container DINode
+ val ovcElem = state.currentInfosetNodeStack.pop
+ val ovcContainer = ovcElem.get.diParent
+ ovcContainer.freeChildIfNoLongerNeeded(ovcContainer.numChildren - 1,
state.releaseUnneededInfoset)
+
+ if (state.currentInfosetNodeStack.isEmpty) {
+ // If there is nothing else on the infoset stack after popping off the
+ // current infoset node, that means we have finished the root element,
+ // so mark the DIDocument as final
+ val doc = state.documentElement
+ Assert.invariant(!doc.isFinal)
+ doc.isFinal = true
+ }
+
move(state)
}
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 1480305..4588eca 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
@@ -454,7 +454,7 @@ class InteractiveDebugger(runner:
InteractiveDebuggerRunner, eCompilers: Express
xml,
walkHidden = !DebuggerConfig.removeHidden,
ignoreBlocks = true,
- removeUnneeded = false)
+ releaseUnneededInfoset = false)
iw.walk(lastWalk = true)
bos.toString("UTF-8")
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
index 6d08e63..0006c4b 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
@@ -410,7 +410,7 @@ class DPathElementCompileInfo(
* different choice branches. Either way, we have to indicate that they are
* ALL referenced by this path step.
*/
- private def indicateReferencedByExpression(matches:
Seq[DPathElementCompileInfo]): Unit = {
+ def indicateReferencedByExpression(matches: Seq[DPathElementCompileInfo]):
Unit = {
matches.foreach { info =>
info.isReferencedByExpressions = true
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
index 4f5a3d1..4d7e816 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
@@ -67,6 +67,8 @@ import org.apache.daffodil.dsom.DPathCompileInfo
sealed trait DINode {
+ def diParent: DINode
+
def asSimple: DISimple = {
this match {
case diSimple: DISimple => diSimple
@@ -134,8 +136,14 @@ sealed trait DINode {
* should only free children if they are not actually needed anymore. It can
* assume that the infoset events have been created, but if, for example, a
* child is used in DPath expressions this should not free the child.
+ *
+ * For testing purposes, the doRelease argument says if it should actually
+ * free the child. If false, this will not free the child and instead just
+ * marks the wouldHaveBeenFreed variable.
*/
- def freeChildIfNoLongerNeeded(index: Int): Unit
+ def freeChildIfNoLongerNeeded(index: Int, doFree: Boolean): Unit
+
+ var wouldHaveBeenFreed: Boolean = false
/**
* When unparsing, arrays and complex elements have a notion of being
finalized.
@@ -151,7 +159,6 @@ sealed trait DINode {
* Array or Complex exception.
*/
def requireFinal: Unit
-
}
/**
@@ -1011,29 +1018,15 @@ final class DIArray(
private lazy val nfe = new InfosetArrayNotFinalException(this)
override def requireFinal: Unit = {
- if (!isFinal) {
- // If this DIArray isn't final, either we haven't gotten all of its
- // children yet, or the array is empty and we'll never get its children.
- // In the former case, we'll eventually get all the children and isFinal
- // will be set to true. However, in the latter case, isFinal will never
- // get set since the InfosetCursorFromXMLEventCursor, which sets the
- // isFinal state, doesn't know anything about the array. So we must check
- // if the parent isFinal. If the parent is final, that means we had a
- // zero-length array, and it should now be marked as final. If the parent
- // isn't final then we could still be wainting for events to come in, so
- // throw an nfe.
- if (parent.isFinal) {
- isFinal = true
- } else {
- throw nfe
- }
- }
+ if (!isFinal) throw nfe
}
override def isSimple = false
override def isComplex = false
override def isArray = true
+ override def diParent = parent
+
// Parsers don't actually call setHidden on DIArrays, only on DIElements. But
// DIArrays are always created when there is at least one child element, and
// since all children of an array must have the same visibility, we can just
@@ -1138,11 +1131,15 @@ final class DIArray(
final def isDefaulted: Boolean = children.forall { _.isDefaulted }
- final def freeChildIfNoLongerNeeded(index: Int): Unit = {
+ final def freeChildIfNoLongerNeeded(index: Int, doFree: Boolean): Unit = {
val node = _contents(index)
if (!node.erd.dpathElementCompileInfo.isReferencedByExpressions) {
- // set to null so that the garbage collector can free this node
- _contents(index) = null
+ if (doFree) {
+ // set to null so that the garbage collector can free this node
+ _contents(index) = null
+ } else {
+ node.wouldHaveBeenFreed = true
+ }
}
}
}
@@ -1411,7 +1408,7 @@ sealed class DISimple(override val erd:
ElementRuntimeData)
Assert.invariantFailed("Should not requireFinal a simple type")
}
- final def freeChildIfNoLongerNeeded(index: Int): Unit = {
+ final def freeChildIfNoLongerNeeded(index: Int, doFree: Boolean): Unit = {
Assert.invariantFailed("Should not try to remove a child of a simple type")
}
@@ -1534,11 +1531,15 @@ sealed class DIComplex(override val erd:
ElementRuntimeData)
}
}
- def freeChildIfNoLongerNeeded(index: Int): Unit = {
+ def freeChildIfNoLongerNeeded(index: Int, doFree: Boolean): Unit = {
val node = childNodes(index)
if (!node.erd.dpathElementCompileInfo.isReferencedByExpressions) {
- // set to null so that the garbage collector can free this node
- childNodes(index) = null
+ if (doFree) {
+ // set to null so that the garbage collector can free this node
+ childNodes(index) = null
+ } else {
+ node.wouldHaveBeenFreed = true
+ }
}
}
@@ -1685,8 +1686,14 @@ sealed class DIComplex(override val erd:
ElementRuntimeData)
// was never used in a schema expression, it will never be found. If the
// appropriate tunable is set, we will do a linear search to find the
// element. Due to the slowness of this, this should only be enabled
- // during debugging or testing.
- val found = childNodes.filter(_.erd.namedQName == qname)
+ // during debugging or testing. Also note that we need to do a null check
+ // since it's possible some children have been freed. Generally if this
+ // tunable is set one should also have debugging enabled or disable
+ // freeing of infoset nodes (see releaseUnneededInfoset). But those
aren't
+ // strictly required, so this avoids an NPE.
+ val found = childNodes.filter { child =>
+ child != null && child.erd.namedQName == qname
+ }
// Daffodil does not support query expressions yet, so there should be at
// most one item found
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetWalker.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetWalker.scala
index 7fd6d1d..5968f14 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetWalker.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetWalker.scala
@@ -51,9 +51,9 @@ object InfosetWalker {
* to prevent creation of infoset events that might be backtracked. This
* should usually only be set to true while debugging
*
- * @param removeUnneeded
+ * @param releaseUnneededInfoset
*
- * Whether or not to remove infoset nodes once it is determined that they
+ * Whether or not to release infoset nodes once it is determined that they
* will no longer be used by Daffodil. This should usually be set to true
* except while debugging
*/
@@ -62,7 +62,7 @@ object InfosetWalker {
outputter: InfosetOutputter,
walkHidden: Boolean,
ignoreBlocks: Boolean,
- removeUnneeded: Boolean): InfosetWalker = {
+ releaseUnneededInfoset: Boolean): InfosetWalker = {
// Determine the container of the root node and the index in which it
// appears in that node
@@ -88,7 +88,7 @@ object InfosetWalker {
outputter,
walkHidden,
ignoreBlocks,
- removeUnneeded)
+ releaseUnneededInfoset)
}
}
@@ -135,7 +135,7 @@ object InfosetWalker {
* to prevent creation of infoset events that might be backtracked. This
* should usually only be set to true while debugging
*
- * @param removeUnneeded
+ * @param releaseUnneededInfoset
*
* Whether or not to remove infoset nodes once it is determined that they
* will no longer be used by Daffodil. This should usually be set to true
@@ -147,7 +147,7 @@ class InfosetWalker private (
val outputter: InfosetOutputter,
walkHidden: Boolean,
ignoreBlocks: Boolean,
- removeUnneeded: Boolean) {
+ releaseUnneededInfoset: Boolean) {
/**
* These two pieces of mutable state are all that is needed to keep track of
@@ -455,10 +455,8 @@ class InfosetWalker private (
outputter.startSimple(simple)
outputter.endSimple(simple)
}
- if (removeUnneeded) {
- // now we can remove this simple element to free up memory
- containerNode.freeChildIfNoLongerNeeded(containerIndex)
- }
+ // now we can remove this simple element to free up memory
+ containerNode.freeChildIfNoLongerNeeded(containerIndex,
releaseUnneededInfoset)
moveToNextSibling()
} else {
// must be complex or array, exact same logic for both
@@ -497,9 +495,7 @@ class InfosetWalker private (
// memory associated with this container, and then move to the next
// sibling of this container
moveToContainer()
- if (removeUnneeded) {
-
containerNodeStack.top.freeChildIfNoLongerNeeded(containerIndexStack.top)
- }
+
containerNodeStack.top.freeChildIfNoLongerNeeded(containerIndexStack.top,
releaseUnneededInfoset)
moveToNextSibling()
}
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
index b75a9a1..63c330b 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
@@ -17,15 +17,19 @@
package org.apache.daffodil.infoset
-import org.apache.daffodil.util.Maybe
-import scala.xml.Null
import scala.collection.mutable.ListBuffer
-import org.apache.daffodil.xml.XMLUtils
-import org.apache.daffodil.util.MStackOf
-import org.apache.daffodil.exceptions.Assert
+import scala.xml.MetaData
+import scala.xml.Null
+import scala.xml.UnprefixedAttribute
+
import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.util.MStackOf
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.xml.XMLUtils
-class ScalaXMLInfosetOutputter(showFormatInfo: Boolean = false) extends
InfosetOutputter
+
+class ScalaXMLInfosetOutputter(showFormatInfo: Boolean = false, showFreedInfo:
Boolean = false) extends InfosetOutputter
with XMLInfosetOutputter {
protected val stack = new MStackOf[ListBuffer[scala.xml.Node]]
@@ -48,32 +52,57 @@ class ScalaXMLInfosetOutputter(showFormatInfo: Boolean =
false) extends InfosetO
true
}
+ private def getAttributes(diElem: DIElement): MetaData = {
+ val nilAttr = if (isNilled(diElem)) XMLUtils.xmlNilAttribute else Null
+ val freedAttr =
+ if (showFreedInfo) {
+ val selfFreed = diElem.wouldHaveBeenFreed
+ val arrayFreed =
+ if (diElem.erd.isArray) diElem.diParent.children.find { _.erd eq
diElem.erd }.get.wouldHaveBeenFreed
+ else false
+ if (selfFreed || arrayFreed) {
+ val freedAttrVal =
+ if (selfFreed && arrayFreed) "self+array"
+ else if (selfFreed) "self"
+ else "array"
+ new UnprefixedAttribute("freed", freedAttrVal, nilAttr)
+ } else {
+ nilAttr
+ }
+ } else {
+ nilAttr
+ }
+ freedAttr
+ }
+
def startSimple(diSimple: DISimple): Boolean = {
- val e =
- if (isNilled(diSimple)) {
- scala.xml.Elem(diSimple.erd.namedQName.prefixOrNull, diSimple.erd.name,
- XMLUtils.xmlNilAttribute, diSimple.erd.minimizedScope,
minimizeEmpty = true)
- } else if (diSimple.hasValue) {
+ val attributes = getAttributes(diSimple)
+
+ val children =
+ if (!isNilled(diSimple) && diSimple.hasValue) {
val text =
if (diSimple.erd.optPrimType.get.isInstanceOf[NodeInfo.String.Kind])
{
remapped(diSimple.dataValueAsString)
} else {
diSimple.dataValueAsString
}
- val textNode = new scala.xml.Text(text)
- scala.xml.Elem(diSimple.erd.namedQName.prefixOrNull,
diSimple.erd.name, Null,
- diSimple.erd.minimizedScope, minimizeEmpty = true, textNode)
+ Seq(new scala.xml.Text(text))
} else {
- // element has been created but has no value yet, display an empty
element tag
- scala.xml.Elem(diSimple.erd.namedQName.prefixOrNull,
diSimple.erd.name, Null,
- diSimple.erd.minimizedScope, minimizeEmpty = true)
+ Seq()
}
- val elem = addFmtInfo(diSimple, e, showFormatInfo)
-
- stack.top.append(elem)
- //returning true/false will be used when recursion is removed
+ val elem =
+ scala.xml.Elem(
+ diSimple.erd.namedQName.prefixOrNull,
+ diSimple.erd.name,
+ attributes,
+ diSimple.erd.minimizedScope,
+ minimizeEmpty = true,
+ children: _*)
+
+ val elemWithFmt = addFmtInfo(diSimple, elem, showFormatInfo)
+ stack.top.append(elemWithFmt)
true
}
@@ -88,21 +117,20 @@ class ScalaXMLInfosetOutputter(showFormatInfo: Boolean =
false) extends InfosetO
def endComplex(diComplex: DIComplex): Boolean = {
+ val attributes = getAttributes(diComplex)
val children = stack.pop
- val e =
- if (isNilled(diComplex)) {
- scala.xml.Elem(diComplex.erd.namedQName.prefixOrNull,
diComplex.erd.name,
- XMLUtils.xmlNilAttribute, diComplex.erd.minimizedScope,
minimizeEmpty = true)
- } else {
- scala.xml.Elem(diComplex.erd.namedQName.prefixOrNull,
diComplex.erd.name,
- scala.xml.Null, diComplex.erd.minimizedScope, minimizeEmpty =
true, children: _*)
- }
-
- val elem = addFmtInfo(diComplex, e, showFormatInfo)
-
- stack.top.append(elem)
- //returning true/false will be used when recursion is removed
+ val elem =
+ scala.xml.Elem(
+ diComplex.erd.namedQName.prefixOrNull,
+ diComplex.erd.name,
+ attributes,
+ diComplex.erd.minimizedScope,
+ minimizeEmpty = true,
+ children: _*)
+
+ val elemWithFmt = addFmtInfo(diComplex, elem, showFormatInfo)
+ stack.top.append(elemWithFmt)
true
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
index 3a47b55..6e79368 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
@@ -562,7 +562,8 @@ class DataProcessor private (
UState.createInitialUState(
out,
this,
- inputter)
+ inputter,
+ areDebugging)
val res = try {
if (areDebugging) {
Assert.invariant(optDebugger.isDefined)
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 6d90d76..99c7194 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
@@ -674,7 +674,7 @@ object PState {
output,
walkHidden = false,
ignoreBlocks = false,
- removeUnneeded = !areDebugging)
+ releaseUnneededInfoset = !areDebugging &&
tunables.releaseUnneededInfoset)
dis.cst.setPriorBitOrder(root.defaultBitOrder)
val newState = new PState(
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 30091ae..888dd55 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
@@ -74,7 +74,8 @@ abstract class UState(
vbox: VariableBox,
diagnosticsArg: List[Diagnostic],
dataProcArg: Maybe[DataProcessor],
- tunable: DaffodilTunables)
+ tunable: DaffodilTunables,
+ areDebugging: Boolean)
extends ParseOrUnparseState(vbox, diagnosticsArg, dataProcArg, tunable)
with Cursor[InfosetAccessor] with ThrowsSDE with SavesErrorsAndWarnings {
@@ -349,6 +350,8 @@ abstract class UState(
def removeVariableInstance(vrd: VariableRuntimeData): Unit = {
variableMap.removeVariableInstance(vrd)
}
+
+ final val releaseUnneededInfoset: Boolean = !areDebugging &&
tunable.releaseUnneededInfoset
}
/**
@@ -368,8 +371,9 @@ final class UStateForSuspension(
escapeSchemeEVCacheMaybe: Maybe[MStackOfMaybe[EscapeSchemeUnparserHelper]],
delimiterStackMaybe: Maybe[MStackOf[DelimiterStackUnparseNode]],
override val prior: UStateForSuspension,
- tunable: DaffodilTunables)
- extends UState(dos, vbox, mainUState.diagnostics, mainUState.dataProc,
tunable) {
+ tunable: DaffodilTunables,
+ areDebugging: Boolean)
+ extends UState(dos, vbox, mainUState.diagnostics, mainUState.dataProc,
tunable, areDebugging) {
dState.setMode(UnparserBlocking)
dState.setCurrentNode(thisElement.asInstanceOf[DINode])
@@ -443,8 +447,9 @@ final class UStateMain private (
diagnosticsArg: List[Diagnostic],
dataProcArg: DataProcessor,
dos: DirectOrBufferedDataOutputStream,
- tunable: DaffodilTunables)
- extends UState(dos, vbox, diagnosticsArg, One(dataProcArg), tunable) {
+ tunable: DaffodilTunables,
+ areDebugging: Boolean)
+ extends UState(dos, vbox, diagnosticsArg, One(dataProcArg), tunable,
areDebugging) {
dState.setMode(UnparserBlocking)
@@ -454,9 +459,10 @@ final class UStateMain private (
diagnosticsArg: List[Diagnostic],
dataProcArg: DataProcessor,
dataOutputStream: DirectOrBufferedDataOutputStream,
- tunable: DaffodilTunables) =
+ tunable: DaffodilTunables,
+ areDebugging: Boolean) =
this(inputter, new VariableBox(vmap), diagnosticsArg, dataProcArg,
- dataOutputStream, tunable)
+ dataOutputStream, tunable, areDebugging)
private var _prior: UStateForSuspension = null
override def prior = _prior
@@ -495,7 +501,8 @@ final class UStateMain private (
es,
ds,
prior,
- tunable)
+ tunable,
+ areDebugging)
clone.setProcessor(processor)
@@ -647,7 +654,8 @@ object UState {
def createInitialUState(
out: DirectOrBufferedDataOutputStream,
dataProc: DFDL.DataProcessor,
- inputter: InfosetInputter): UStateMain = {
+ inputter: InfosetInputter,
+ areDebugging: Boolean): UStateMain = {
Assert.invariant(inputter.isInitialized)
/**
@@ -657,8 +665,14 @@ object UState {
val variables = dataProc.variableMap.copy
val diagnostics = Nil
- val newState = new UStateMain(inputter, variables, diagnostics,
dataProc.asInstanceOf[DataProcessor], out,
- dataProc.getTunables()) // null means no prior UState
+ val newState = new UStateMain(
+ inputter,
+ variables,
+ diagnostics,
+ dataProc.asInstanceOf[DataProcessor],
+ out,
+ dataProc.getTunables(),
+ areDebugging)
newState
}
}