You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sl...@apache.org on 2022/02/10 17:18:43 UTC

[daffodil] branch main updated: Fix fn:exists when unparsing non-blocking expressions

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 3314995  Fix fn:exists when unparsing non-blocking expressions
3314995 is described below

commit 331499562a12a741d761b518e5ae3b0838b47ba8
Author: Steve Lawrence <sl...@apache.org>
AuthorDate: Wed Feb 9 10:46:46 2022 -0500

    Fix fn:exists when unparsing non-blocking expressions
    
    Unparsing non-blocking expressions represent expression that are only
    backwards referencing and will not require a suspension. This means that
    the associated node must be final, and so we can immediately respond
    that the element exists or not. Rethrowing the associated exception not
    correct in this case. That is only correct when suspensions will be
    involve, in which case that exception triggers the suspension logic.
    
    DAFFODIL-2648
---
 .../org/apache/daffodil/dpath/FNFunctions.scala    |  23 ++--
 .../section23/dfdl_functions/Functions.tdml        | 136 +++++++++++++++++++++
 .../dfdl_expressions/TestDFDLExpressions.scala     |  14 ++-
 3 files changed, 158 insertions(+), 15 deletions(-)

diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
index 2fc7c46..af4f537 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
@@ -553,16 +553,20 @@ trait ExistsKind {
     Assert.invariant(dstate.currentNode ne null)
     val res = dstate.mode match {
       case UnparserNonBlocking => {
-        // we are evaluating the expression, and hoping it will evaluate
-        // completely just to avoid the overhead of creating a suspension
-        //
-        // we will always re-throw in this case so that fn:exists doesn't
-        // return false, resulting in the expression getting a value that
-        // might, later, have returned true.
-        //
-        throw th
+        // we are evaluating an expresion while unparsing in non blocking mode.
+        // This means this throwable must have come from an evaluatable using
+        // only backwards references. Backwards references are known to be
+        // final (even if isFinal isn't set yet), so because an exception was
+        // thrown, this element must not exist.
+        false
       }
       case UnparserBlocking => {
+        // we are evaluating an expression while unparsing in blocking mode.
+        // This means we don't know if this exception means the element does
+        // not exist or just hasn't been created in the infoset yet. If the
+        // current node is final, then we are sure it doesn't exist. Otherwise
+        // we throw the exception which triggers suspension logic to retry
+        // later
         dstate.currentNode match {
           case c: DINode if (c.isFinal) => false
           case _ => throw th
@@ -582,7 +586,8 @@ trait ExistsKind {
     // have blocked but didn't.
     //
     dstate.mode match {
-      case UnparserBlocking | UnparserNonBlocking => {
+      case UnparserNonBlocking => // ok, fall through
+      case UnparserBlocking => {
         th match {
           case u: UnsuppressableException => throw u
           case r: RetryableException => // ok fall through
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
index fc2e4b8..146e570 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
@@ -4748,6 +4748,17 @@
       </xs:complexType>
     </xs:element>
 
+    <xs:element name="exists09">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" minOccurs="0" dfdl:lengthKind="explicit" dfdl:length="1" />
+          <xs:element name="term" type="xs:string"
+            dfdl:lengthKind="explicit" dfdl:length="0"
+            dfdl:terminator="{ if (fn:exists(../ex:int)) then 'x' else 'y' }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
     <xs:element name="exactly-one01">
       <xs:complexType>
         <xs:sequence dfdl:separator="|">
@@ -4926,6 +4937,17 @@
       </xs:complexType>
     </xs:element>
 
+    <xs:element name="count06">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" minOccurs="0" maxOccurs="2" dfdl:lengthKind="explicit" dfdl:length="1" />
+          <xs:element name="term" type="xs:string"
+            dfdl:lengthKind="explicit" dfdl:length="0"
+            dfdl:terminator="{ if (fn:count(../ex:int) gt 1) then 'x' else 'y' }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
     <xs:element name="local-name01">
       <xs:complexType>
         <xs:sequence dfdl:separator=",">
@@ -10531,6 +10553,51 @@
   </tdml:parserTestCase>
 
 <!--
+    Test Name: exists_13
+       Schema: XPathFunctions
+         Root: exists09
+         Purpose: This test demonstrates the exists() function works as expected
+                  when unparsing in something other than an output value calc expression
+-->
+
+  <tdml:unparserTestCase name="exists_13" root="exists09"
+    model="XPathFunctions" description="Section 23 - Functions - fn:exists - DFDL-23-122R">
+    <tdml:document>
+      <tdml:documentPart type="text">1x</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <exists09 xmlns="http://example.com">
+          <int>1</int>
+          <term></term>
+        </exists09>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+<!--
+    Test Name: exists_14
+       Schema: XPathFunctions
+         Root: exists09
+         Purpose: This test demonstrates the exists() function works as expected
+                  when unparsing in something other than an output value calc expression
+-->
+
+  <tdml:unparserTestCase name="exists_14" root="exists09"
+    model="XPathFunctions" description="Section 23 - Functions - fn:exists - DFDL-23-122R">
+    <tdml:document>
+      <tdml:documentPart type="text">y</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <exists09 xmlns="http://example.com">
+          <term></term>
+        </exists09>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+<!--
     Test Name: exactly_one_01
        Schema: XPathFunctions
          Root: exactly-one01
@@ -11049,6 +11116,75 @@
   </tdml:parserTestCase>
 
 <!--
+    Test Name: count_09
+       Schema: XPathFunctions
+         Root: count06
+         Purpose: This test demonstrates the count() function works as expected
+                  when unparsing in something other than an output value calc expression
+-->
+
+  <tdml:unparserTestCase name="count_09" root="count06"
+    model="XPathFunctions" description="Section 23 - Functions - fn:count - DFDL-23-122R">
+    <tdml:document>
+      <tdml:documentPart type="text">y</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <count06 xmlns="http://example.com">
+          <term></term>
+        </count06>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+<!--
+    Test Name: count_10
+       Schema: XPathFunctions
+         Root: count06
+         Purpose: This test demonstrates the count() function works as expected
+                  when unparsing in something other than an output value calc expression
+-->
+
+  <tdml:unparserTestCase name="count_10" root="count06"
+    model="XPathFunctions" description="Section 23 - Functions - fn:count - DFDL-23-122R">
+    <tdml:document>
+      <tdml:documentPart type="text">1y</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <count06 xmlns="http://example.com">
+          <int>1</int>
+          <term></term>
+        </count06>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+<!--
+    Test Name: count_11
+       Schema: XPathFunctions
+         Root: count06
+         Purpose: This test demonstrates the count() function works as expected
+                  when unparsing in something other than an output value calc expression
+-->
+
+  <tdml:unparserTestCase name="count_11" root="count06"
+    model="XPathFunctions" description="Section 23 - Functions - fn:count - DFDL-23-122R">
+    <tdml:document>
+      <tdml:documentPart type="text">12x</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <count06 xmlns="http://example.com">
+          <int>1</int>
+          <int>2</int>
+          <term></term>
+        </count06>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+<!--
     Test Name: local_name_01
        Schema: XPathFunctions
          Root: local-name01
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
index 22cad5f..f10f77e 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
@@ -478,11 +478,11 @@ class TestDFDLExpressions {
   @Test def test_exists_05(): Unit = { runner2.runOneTest("exists_05") }
   @Test def test_exists_08(): Unit = { runner2.runOneTest("exists_08") }
   @Test def test_exists_09(): Unit = { runner2.runOneTest("exists_09") }
-
   @Test def test_exists_10(): Unit = { runner2.runOneTest("exists_10") }
-
   @Test def test_exists_11(): Unit = { runner2.runOneTest("exists_11") }
   @Test def test_exists_12(): Unit = { runner2.runOneTest("exists_12") }
+  @Test def test_exists_13(): Unit = { runner2.runOneTest("exists_13") }
+  @Test def test_exists_14(): Unit = { runner2.runOneTest("exists_14") }
 
   //DFDL-1189
   //@Test def test_exactly_one_01() { runner2.runOneTest("exactly_one_01") }
@@ -497,14 +497,16 @@ class TestDFDLExpressions {
   @Test def test_count_03(): Unit = { runner2.runOneTest("count_03") }
   @Test def test_count_03b(): Unit = { runner2.runOneTest("count_03b") }
   @Test def test_count_04(): Unit = { runner2.runOneTest("count_04") }
+  @Test def test_count_05(): Unit = { runner2.runOneTest("count_05") }
   @Test def test_count_05c(): Unit = { runner2.runOneTest("count_05c") }
+  @Test def test_count_06(): Unit = { runner2.runOneTest("count_06") }
   @Test def test_count_06b(): Unit = { runner2.runOneTest("count_06b") }
   @Test def test_count_07(): Unit = { runner2.runOneTest("count_07") }
-  @Test def test_count_08b(): Unit = { runner2.runOneTest("count_08b") }
-
-  @Test def test_count_05(): Unit = { runner2.runOneTest("count_05") }
-  @Test def test_count_06(): Unit = { runner2.runOneTest("count_06") }
   @Test def test_count_08(): Unit = { runner2.runOneTest("count_08") }
+  @Test def test_count_08b(): Unit = { runner2.runOneTest("count_08b") }
+  @Test def test_count_09(): Unit = { runner2.runOneTest("count_09") }
+  @Test def test_count_10(): Unit = { runner2.runOneTest("count_10") }
+  @Test def test_count_11(): Unit = { runner2.runOneTest("count_11") }
 
   @Test def test_local_name_01(): Unit = { runner2.runOneTest("local_name_01") }
   @Test def test_local_name_02(): Unit = { runner2.runOneTest("local_name_02") }