[libxslt] Move function result RVTs to context variable



commit 5e16672db1188accbde737f0add01213ffed107e
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Wed Sep 26 23:20:48 2018 +0200

    Move function result RVTs to context variable
    
    If a variable with a "select" expression calls an EXSLT func:function,
    the context variable must be restored before evaluating the function
    result. This makes sure that the RVTs in the result will be moved to
    the context variable's fragment list when they're released in
    xsltReleaseLocalRVTs or xsltReleaseLocalRVTs.
    
    Thanks to Nikolai Weibull for the report.

 libexslt/functions.c      | 23 ++++++++++++-----------
 libxslt/transform.c       | 25 +++++++++++--------------
 tests/docs/bug-212.xml    |  1 +
 tests/docs/bug-213.xml    |  1 +
 tests/general/bug-212.out |  1 +
 tests/general/bug-212.xsl | 25 +++++++++++++++++++++++++
 tests/general/bug-213.out |  1 +
 tests/general/bug-213.xsl | 26 ++++++++++++++++++++++++++
 8 files changed, 78 insertions(+), 25 deletions(-)
---
diff --git a/libexslt/functions.c b/libexslt/functions.c
index b7b968f8..60056717 100644
--- a/libexslt/functions.c
+++ b/libexslt/functions.c
@@ -34,6 +34,7 @@ typedef struct _exsltFuncData exsltFuncData;
 struct _exsltFuncData {
     xmlHashTablePtr funcs;     /* pointer to the stylesheet module data */
     xmlXPathObjectPtr result;  /* returned by func:result */
+    xsltStackElemPtr ctxtVar;   /* context variable */
     int error;                 /* did an error occur? */
 };
 
@@ -426,28 +427,23 @@ exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
        }
     }
     /*
-     * Actual processing. Note that contextVariable is set to NULL which
-     * means that RVTs returned from functions always end up as local RVTs,
-     * not as variable fragments if the function is called in the select
-     * expression of an xsl:variable. This is a hack that only works because
-     * xsltReleaseLocalRVTs isn't called after processing xsl:variable.
-     *
-     * It would probably be better to remove the fragile contextVariable
-     * logic and make xsltEvalVariable move the required RVTs into the
-     * variable manually.
+     * Actual processing. The context variable is cleared and restored
+     * when func:result is evaluated.
      */
     fake = xmlNewDocNode(tctxt->output, NULL,
                         (const xmlChar *)"fake", NULL);
     oldInsert = tctxt->insert;
-    oldCtxtVar = tctxt->contextVariable;
+    oldCtxtVar = data->ctxtVar;
+    data->ctxtVar = tctxt->contextVariable;
     tctxt->insert = fake;
     tctxt->contextVariable = NULL;
     xsltApplyOneTemplate (tctxt, tctxt->node,
                          func->content, NULL, NULL);
     xsltLocalVariablePop(tctxt, tctxt->varsBase, -2);
     tctxt->insert = oldInsert;
-    tctxt->contextVariable = oldCtxtVar;
+    tctxt->contextVariable = data->ctxtVar;
     tctxt->varsBase = oldBase; /* restore original scope */
+    data->ctxtVar = oldCtxtVar;
     if (params != NULL)
        xsltFreeStackElemList(params);
 
@@ -714,6 +710,11 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
        data->error = 1;
        return;
     }
+    /*
+     * Restore context variable, so that it will receive the function
+     * result RVTs.
+     */
+    ctxt->contextVariable = data->ctxtVar;
     /*
      * Processing
      */
diff --git a/libxslt/transform.c b/libxslt/transform.c
index d7af31f1..ed5afacb 100644
--- a/libxslt/transform.c
+++ b/libxslt/transform.c
@@ -2295,13 +2295,17 @@ static void
 xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
 {
     xmlDocPtr cur = ctxt->localRVT, tmp;
-    xmlDocPtr prev = NULL;
 
     if (cur == base)
         return;
     if (cur->prev != NULL)
         xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n");
 
+    /* Reset localRVT early because some RVTs might be registered again. */
+    ctxt->localRVT = base;
+    if (base != NULL)
+        base->prev = NULL;
+
     do {
         tmp = cur;
         cur = (xmlDocPtr) cur->next;
@@ -2310,25 +2314,18 @@ xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
         } else if (tmp->psvi == XSLT_RVT_GLOBAL) {
             xsltRegisterPersistRVT(ctxt, tmp);
         } else if (tmp->psvi == XSLT_RVT_FUNC_RESULT) {
-            if (prev == NULL)
-                ctxt->localRVT = tmp;
-            else
-                prev->next = (xmlNodePtr) tmp;
-            tmp->prev = (xmlNodePtr) prev;
-            prev = tmp;
+            /*
+             * This will either register the RVT again or move it to the
+             * context variable.
+             */
+            xsltRegisterLocalRVT(ctxt, tmp);
+            tmp->psvi = XSLT_RVT_FUNC_RESULT;
         } else {
             xmlGenericError(xmlGenericErrorContext,
                     "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n",
                     tmp->psvi);
         }
     } while (cur != base);
-
-    if (prev == NULL)
-        ctxt->localRVT = base;
-    else
-        prev->next = (xmlNodePtr) base;
-    if (base != NULL)
-        base->prev = (xmlNodePtr) prev;
 }
 
 /**
diff --git a/tests/docs/bug-212.xml b/tests/docs/bug-212.xml
new file mode 100644
index 00000000..69d62f2c
--- /dev/null
+++ b/tests/docs/bug-212.xml
@@ -0,0 +1 @@
+<doc/>
diff --git a/tests/docs/bug-213.xml b/tests/docs/bug-213.xml
new file mode 100644
index 00000000..69d62f2c
--- /dev/null
+++ b/tests/docs/bug-213.xml
@@ -0,0 +1 @@
+<doc/>
diff --git a/tests/general/bug-212.out b/tests/general/bug-212.out
new file mode 100644
index 00000000..13d91365
--- /dev/null
+++ b/tests/general/bug-212.out
@@ -0,0 +1 @@
+a,a
diff --git a/tests/general/bug-212.xsl b/tests/general/bug-212.xsl
new file mode 100644
index 00000000..d8471c4e
--- /dev/null
+++ b/tests/general/bug-212.xsl
@@ -0,0 +1,25 @@
+<xsl:stylesheet version="1.0"
+              xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+              xmlns:func="http://exslt.org/functions";
+              xmlns:a="a"
+              extension-element-prefixes="func">
+<xsl:output method="text" encoding="UTF-8"/>
+
+<func:function name="a:a">
+  <func:result>
+    <xsl:apply-templates mode="a"/>
+  </func:result>
+</func:function>
+
+<xsl:template mode="a" match="node()">
+  <xsl:text>a</xsl:text>
+</xsl:template>
+
+<xsl:template match="/">
+  <xsl:variable name="a" select="a:a()"/>
+  <xsl:value-of select="$a"/>
+  <xsl:text>,</xsl:text>
+  <xsl:value-of select="$a"/>
+  <xsl:text>&#x0a;</xsl:text>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/tests/general/bug-213.out b/tests/general/bug-213.out
new file mode 100644
index 00000000..13d91365
--- /dev/null
+++ b/tests/general/bug-213.out
@@ -0,0 +1 @@
+a,a
diff --git a/tests/general/bug-213.xsl b/tests/general/bug-213.xsl
new file mode 100644
index 00000000..cfad3c51
--- /dev/null
+++ b/tests/general/bug-213.xsl
@@ -0,0 +1,26 @@
+<xsl:stylesheet version="1.0"
+              xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+              xmlns:func="http://exslt.org/functions";
+              xmlns:a="a"
+              extension-element-prefixes="func">
+<xsl:output method="text" encoding="UTF-8"/>
+
+<func:function name="a:a">
+  <xsl:variable name="v">
+    <xsl:apply-templates mode="a"/>
+  </xsl:variable>
+  <func:result select="$v"/>
+</func:function>
+
+<xsl:template mode="a" match="node()">
+  <xsl:text>a</xsl:text>
+</xsl:template>
+
+<xsl:template match="/">
+  <xsl:variable name="a" select="a:a()"/>
+  <xsl:value-of select="$a"/>
+  <xsl:text>,</xsl:text>
+  <xsl:value-of select="$a"/>
+  <xsl:text>&#x0a;</xsl:text>
+</xsl:template>
+</xsl:stylesheet>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]