[libxml2] Improve XPath predicate and filter evaluation
- From: Nick Wellnhofer <nwellnhof src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libxml2] Improve XPath predicate and filter evaluation
- Date: Mon, 22 Apr 2019 12:52:26 +0000 (UTC)
commit c2f4da1a9328251e9171a48b19b95ce4c699460f
Author: Nick Wellnhofer <wellnhofer aevum de>
Date: Sun May 21 22:08:50 2017 +0200
Improve XPath predicate and filter evaluation
Consolidate code paths evaluating XPath predicates and filters.
Don't push context node on stack when evaluating predicates. I have no
idea why this was done. It seems completely useless and trying to pop
the context node from a corrupted stack has already caused security
issues.
Filter nodesets in-place and don't create node sets with NULL gaps which
allows to simplify merging a great deal. Simply move matched nodes
backward and create a compact node set.
Merge xmlXPathCompOpEvalPositionalPredicate into
xmlXPathCompOpEvalPredicate.
result/XPath/tests/chaptersbase | 28 +
test/XPath/tests/chaptersbase | 4 +
xpath.c | 1178 ++++++++++-----------------------------
3 files changed, 335 insertions(+), 875 deletions(-)
---
diff --git a/result/XPath/tests/chaptersbase b/result/XPath/tests/chaptersbase
index fd021d8a..2d8b10c6 100644
--- a/result/XPath/tests/chaptersbase
+++ b/result/XPath/tests/chaptersbase
@@ -138,3 +138,31 @@ Set contains 0 nodes:
Expression: //p[-100000000000000000000]
Object is a Node Set :
Set contains 0 nodes:
+
+========================
+Expression: //chapter[true()][position() mod 2 = 1][true()][2]
+Object is a Node Set :
+Set contains 1 nodes:
+1 ELEMENT chapter
+ ATTRIBUTE id
+ TEXT
+ content=chapter3
+
+========================
+Expression: //chapter[true()][2][true()]
+Object is a Node Set :
+Set contains 1 nodes:
+1 ELEMENT chapter
+ ATTRIBUTE id
+ TEXT
+ content=chapter2
+
+========================
+Expression: //node()[false()]
+Object is a Node Set :
+Set contains 0 nodes:
+
+========================
+Expression: (//node())[false()]
+Object is a Node Set :
+Set contains 0 nodes:
diff --git a/test/XPath/tests/chaptersbase b/test/XPath/tests/chaptersbase
index f8fbe2a8..49900f09 100644
--- a/test/XPath/tests/chaptersbase
+++ b/test/XPath/tests/chaptersbase
@@ -11,3 +11,7 @@
//p[0 div 0]
//p[100000000000000000000]
//p[-100000000000000000000]
+//chapter[true()][position() mod 2 = 1][true()][2]
+//chapter[true()][2][true()]
+//node()[false()]
+(//node())[false()]
diff --git a/xpath.c b/xpath.c
index 4647ab0e..aa10fbfd 100644
--- a/xpath.c
+++ b/xpath.c
@@ -3583,37 +3583,6 @@ xmlXPathNodeSetCreate(xmlNodePtr val) {
return(ret);
}
-/**
- * xmlXPathNodeSetCreateSize:
- * @size: the initial size of the set
- *
- * Create a new xmlNodeSetPtr of type double and of value @val
- *
- * Returns the newly created object.
- */
-static xmlNodeSetPtr
-xmlXPathNodeSetCreateSize(int size) {
- xmlNodeSetPtr ret;
-
- ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
- if (ret == NULL) {
- xmlXPathErrMemory(NULL, "creating nodeset\n");
- return(NULL);
- }
- memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
- if (size < XML_NODESET_DEFAULT)
- size = XML_NODESET_DEFAULT;
- ret->nodeTab = (xmlNodePtr *) xmlMalloc(size * sizeof(xmlNodePtr));
- if (ret->nodeTab == NULL) {
- xmlXPathErrMemory(NULL, "creating nodeset\n");
- xmlFree(ret);
- return(NULL);
- }
- memset(ret->nodeTab, 0 , size * (size_t) sizeof(xmlNodePtr));
- ret->nodeMax = size;
- return(ret);
-}
-
/**
* xmlXPathNodeSetContains:
* @cur: the node-set
@@ -3954,49 +3923,23 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
* xmlXPathNodeSetMergeAndClear:
* @set1: the first NodeSet or NULL
* @set2: the second NodeSet
- * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
*
- * Merges two nodesets, all nodes from @set2 are added to @set1
- * if @set1 is NULL, a new set is created and copied from @set2.
+ * Merges two nodesets, all nodes from @set2 are added to @set1.
* Checks for duplicate nodes. Clears set2.
*
* Returns @set1 once extended or NULL in case of error.
*/
static xmlNodeSetPtr
-xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
- int hasNullEntries)
+xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
{
- if ((set1 == NULL) && (hasNullEntries == 0)) {
- /*
- * Note that doing a memcpy of the list, namespace nodes are
- * just assigned to set1, since set2 is cleared anyway.
- */
- set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
- if (set1 == NULL)
- return(NULL);
- if (set2->nodeNr != 0) {
- memcpy(set1->nodeTab, set2->nodeTab,
- set2->nodeNr * sizeof(xmlNodePtr));
- set1->nodeNr = set2->nodeNr;
- }
- } else {
+ {
int i, j, initNbSet1;
xmlNodePtr n1, n2;
- if (set1 == NULL)
- set1 = xmlXPathNodeSetCreate(NULL);
- if (set1 == NULL)
- return (NULL);
-
initNbSet1 = set1->nodeNr;
for (i = 0;i < set2->nodeNr;i++) {
n2 = set2->nodeTab[i];
/*
- * Skip NULLed entries.
- */
- if (n2 == NULL)
- continue;
- /*
* Skip duplicates.
*/
for (j = 0; j < initNbSet1; j++) {
@@ -4061,49 +4004,21 @@ skip_node:
* xmlXPathNodeSetMergeAndClearNoDupls:
* @set1: the first NodeSet or NULL
* @set2: the second NodeSet
- * @hasSet2NsNodes: 1 if set2 contains namespaces nodes
*
- * Merges two nodesets, all nodes from @set2 are added to @set1
- * if @set1 is NULL, a new set is created and copied from @set2.
- * Doesn't chack for duplicate nodes. Clears set2.
+ * Merges two nodesets, all nodes from @set2 are added to @set1.
+ * Doesn't check for duplicate nodes. Clears set2.
*
* Returns @set1 once extended or NULL in case of error.
*/
static xmlNodeSetPtr
-xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
- int hasNullEntries)
+xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2)
{
- if (set2 == NULL)
- return(set1);
- if ((set1 == NULL) && (hasNullEntries == 0)) {
- /*
- * Note that doing a memcpy of the list, namespace nodes are
- * just assigned to set1, since set2 is cleared anyway.
- */
- set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
- if (set1 == NULL)
- return(NULL);
- if (set2->nodeNr != 0) {
- memcpy(set1->nodeTab, set2->nodeTab,
- set2->nodeNr * sizeof(xmlNodePtr));
- set1->nodeNr = set2->nodeNr;
- }
- } else {
+ {
int i;
xmlNodePtr n2;
- if (set1 == NULL)
- set1 = xmlXPathNodeSetCreate(NULL);
- if (set1 == NULL)
- return (NULL);
-
for (i = 0;i < set2->nodeNr;i++) {
n2 = set2->nodeTab[i];
- /*
- * Skip NULLed entries.
- */
- if (n2 == NULL)
- continue;
if (set1->nodeMax == 0) {
set1->nodeTab = (xmlNodePtr *) xmlMalloc(
XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
@@ -7654,7 +7569,7 @@ typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
* Used for merging node sets in xmlXPathCollectAndTest().
*/
typedef xmlNodeSetPtr (*xmlXPathNodeSetMergeFunction)
- (xmlNodeSetPtr, xmlNodeSetPtr, int);
+ (xmlNodeSetPtr, xmlNodeSetPtr);
/**
@@ -11654,366 +11569,296 @@ xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
}
#endif /* DEBUG_STEP */
-static int
-xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- xmlNodeSetPtr set,
- int contextSize,
- int hasNsNodes)
+/**
+ * xmlXPathNodeSetFilter:
+ * @ctxt: the XPath Parser context
+ * @set: the node set to filter
+ * @filterOpIndex: the index of the predicate/filter op
+ * @minPos: minimum position in the filtered set (1-based)
+ * @maxPos: maximum position in the filtered set (1-based)
+ * @hasNsNodes: true if the node set may contain namespace nodes
+ *
+ * Filter a node set, keeping only nodes for which the predicate expression
+ * matches. Afterwards, keep only nodes between minPos and maxPos in the
+ * filtered result.
+ */
+static void
+xmlXPathNodeSetFilter(xmlXPathParserContextPtr ctxt,
+ xmlNodeSetPtr set,
+ int filterOpIndex,
+ int minPos, int maxPos,
+ int hasNsNodes)
{
- if (op->ch1 != -1) {
- xmlXPathCompExprPtr comp = ctxt->comp;
- /*
- * Process inner predicates first.
- */
- if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
- /*
- * TODO: raise an internal error.
- */
- }
- contextSize = xmlXPathCompOpEvalPredicate(ctxt,
- &comp->steps[op->ch1], set, contextSize, hasNsNodes);
- CHECK_ERROR0;
- if (contextSize <= 0)
- return(0);
+ xmlXPathContextPtr xpctxt;
+ xmlNodePtr oldnode;
+ xmlDocPtr olddoc;
+ xmlXPathStepOpPtr filterOp;
+ int oldcs, oldpp;
+ int i, j, pos;
+
+ if ((set == NULL) || (set->nodeNr == 0))
+ return;
+
+ /*
+ * Check if the node set contains a sufficient number of nodes for
+ * the requested range.
+ */
+ if (set->nodeNr < minPos) {
+ xmlXPathNodeSetClear(set, hasNsNodes);
+ return;
}
- if (op->ch2 != -1) {
- xmlXPathContextPtr xpctxt = ctxt->context;
- xmlNodePtr contextNode, oldContextNode;
- xmlDocPtr oldContextDoc;
- int oldcs, oldpp;
- int i, res, contextPos = 0, newContextSize;
- xmlXPathStepOpPtr exprOp;
- xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
-#ifdef LIBXML_XPTR_ENABLED
- /*
- * URGENT TODO: Check the following:
- * We don't expect location sets if evaluating prediates, right?
- * Only filters should expect location sets, right?
- */
-#endif
- /*
- * SPEC XPath 1.0:
- * "For each node in the node-set to be filtered, the
- * PredicateExpr is evaluated with that node as the
- * context node, with the number of nodes in the
- * node-set as the context size, and with the proximity
- * position of the node in the node-set with respect to
- * the axis as the context position;"
- * @oldset is the node-set" to be filtered.
- *
- * SPEC XPath 1.0:
- * "only predicates change the context position and
- * context size (see [2.4 Predicates])."
- * Example:
- * node-set context pos
- * nA 1
- * nB 2
- * nC 3
- * After applying predicate [position() > 1] :
- * node-set context pos
- * nB 1
- * nC 2
- */
- oldContextNode = xpctxt->node;
- oldContextDoc = xpctxt->doc;
- oldcs = xpctxt->contextSize;
- oldpp = xpctxt->proximityPosition;
- /*
- * Get the expression of this predicate.
- */
- exprOp = &ctxt->comp->steps[op->ch2];
- newContextSize = 0;
- for (i = 0; i < set->nodeNr; i++) {
- if (set->nodeTab[i] == NULL)
- continue;
+ xpctxt = ctxt->context;
+ oldnode = xpctxt->node;
+ olddoc = xpctxt->doc;
+ oldcs = xpctxt->contextSize;
+ oldpp = xpctxt->proximityPosition;
+ filterOp = &ctxt->comp->steps[filterOpIndex];
- contextNode = set->nodeTab[i];
- xpctxt->node = contextNode;
- xpctxt->contextSize = contextSize;
- xpctxt->proximityPosition = ++contextPos;
+ xpctxt->contextSize = set->nodeNr;
- /*
- * Also set the xpath document in case things like
- * key() are evaluated in the predicate.
- */
- if ((contextNode->type != XML_NAMESPACE_DECL) &&
- (contextNode->doc != NULL))
- xpctxt->doc = contextNode->doc;
- /*
- * Evaluate the predicate expression with 1 context node
- * at a time; this node is packaged into a node set; this
- * node set is handed over to the evaluation mechanism.
- */
- if (contextObj == NULL)
- contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
- else {
- if (xmlXPathNodeSetAddUnique(contextObj->nodesetval,
- contextNode) < 0) {
- ctxt->error = XPATH_MEMORY_ERROR;
- goto evaluation_exit;
- }
- }
+ for (i = 0, j = 0, pos = 1; i < set->nodeNr; i++) {
+ xmlNodePtr node = set->nodeTab[i];
+ int res;
- valuePush(ctxt, contextObj);
+ xpctxt->node = node;
+ xpctxt->proximityPosition = i + 1;
- res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
+ /*
+ * Also set the xpath document in case things like
+ * key() are evaluated in the predicate.
+ *
+ * TODO: Get real doc for namespace nodes.
+ */
+ if ((node->type != XML_NAMESPACE_DECL) &&
+ (node->doc != NULL))
+ xpctxt->doc = node->doc;
- if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
- xmlXPathNodeSetClear(set, hasNsNodes);
- newContextSize = 0;
- goto evaluation_exit;
- }
+ res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
- if (res != 0) {
- newContextSize++;
- } else {
- /*
- * Remove the entry from the initial node set.
- */
- set->nodeTab[i] = NULL;
- if (contextNode->type == XML_NAMESPACE_DECL)
- xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
- }
- if (ctxt->value == contextObj) {
- /*
- * Don't free the temporary XPath object holding the
- * context node, in order to avoid massive recreation
- * inside this loop.
- */
- valuePop(ctxt);
- xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
- } else {
- /*
- * TODO: The object was lost in the evaluation machinery.
- * Can this happen? Maybe in internal-error cases.
- */
- contextObj = NULL;
- }
- }
+ if (ctxt->error != XPATH_EXPRESSION_OK)
+ goto exit;
+ if (res < 0) {
+ /* Shouldn't happen */
+ xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
+ goto exit;
+ }
- if (contextObj != NULL) {
- if (ctxt->value == contextObj)
- valuePop(ctxt);
- xmlXPathReleaseObject(xpctxt, contextObj);
- }
-evaluation_exit:
- if (exprRes != NULL)
- xmlXPathReleaseObject(ctxt->context, exprRes);
- /*
- * Reset/invalidate the context.
- */
- xpctxt->node = oldContextNode;
- xpctxt->doc = oldContextDoc;
- xpctxt->contextSize = oldcs;
- xpctxt->proximityPosition = oldpp;
- return(newContextSize);
- }
- return(contextSize);
-}
+ if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
+ if (i != j) {
+ set->nodeTab[j] = node;
+ set->nodeTab[i] = NULL;
+ }
-static int
-xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op,
- xmlNodeSetPtr set,
- int contextSize,
- int minPos,
- int maxPos,
- int hasNsNodes)
-{
- if (op->ch1 != -1) {
- xmlXPathCompExprPtr comp = ctxt->comp;
- if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
- /*
- * TODO: raise an internal error.
- */
- }
- contextSize = xmlXPathCompOpEvalPredicate(ctxt,
- &comp->steps[op->ch1], set, contextSize, hasNsNodes);
- CHECK_ERROR0;
- if (contextSize <= 0)
- return(0);
+ j += 1;
+ } else {
+ /* Remove the entry from the initial node set. */
+ set->nodeTab[i] = NULL;
+ if (node->type == XML_NAMESPACE_DECL)
+ xmlXPathNodeSetFreeNs((xmlNsPtr) node);
+ }
+
+ if (res != 0) {
+ if (pos == maxPos) {
+ /* Clear remaining nodes and exit loop. */
+ if (hasNsNodes) {
+ for (i++; i < set->nodeNr; i++) {
+ node = set->nodeTab[i];
+ if ((node != NULL) &&
+ (node->type == XML_NAMESPACE_DECL))
+ xmlXPathNodeSetFreeNs((xmlNsPtr) node);
+ }
+ }
+ break;
+ }
+
+ pos += 1;
+ }
}
- /*
- * Check if the node set contains a sufficient number of nodes for
- * the requested range.
- */
- if (contextSize < minPos) {
- xmlXPathNodeSetClear(set, hasNsNodes);
- return(0);
+
+ set->nodeNr = j;
+
+ /* If too many elements were removed, shrink table to preserve memory. */
+ if ((set->nodeMax > XML_NODESET_DEFAULT) &&
+ (set->nodeNr < set->nodeMax / 2)) {
+ xmlNodePtr *tmp;
+ int nodeMax = set->nodeNr;
+
+ if (nodeMax < XML_NODESET_DEFAULT)
+ nodeMax = XML_NODESET_DEFAULT;
+ tmp = (xmlNodePtr *) xmlRealloc(set->nodeTab,
+ nodeMax * sizeof(xmlNodePtr));
+ if (tmp == NULL) {
+ xmlXPathPErrMemory(ctxt, "shrinking nodeset\n");
+ } else {
+ set->nodeTab = tmp;
+ set->nodeMax = nodeMax;
+ }
}
- if (op->ch2 == -1) {
- /*
- * TODO: Can this ever happen?
- */
- return (contextSize);
- } else {
- xmlDocPtr oldContextDoc;
- int oldcs, oldpp;
- int i, pos = 0, newContextSize = 0, contextPos = 0, res;
- xmlXPathStepOpPtr exprOp;
- xmlXPathObjectPtr contextObj = NULL, exprRes = NULL;
- xmlNodePtr oldContextNode, contextNode = NULL;
- xmlXPathContextPtr xpctxt = ctxt->context;
- int frame;
+
+exit:
+ xpctxt->node = oldnode;
+ xpctxt->doc = olddoc;
+ xpctxt->contextSize = oldcs;
+ xpctxt->proximityPosition = oldpp;
+}
#ifdef LIBXML_XPTR_ENABLED
- /*
- * URGENT TODO: Check the following:
- * We don't expect location sets if evaluating prediates, right?
- * Only filters should expect location sets, right?
- */
-#endif /* LIBXML_XPTR_ENABLED */
+/**
+ * xmlXPathLocationSetFilter:
+ * @ctxt: the XPath Parser context
+ * @locset: the location set to filter
+ * @filterOpIndex: the index of the predicate/filter op
+ * @minPos: minimum position in the filtered set (1-based)
+ * @maxPos: maximum position in the filtered set (1-based)
+ *
+ * Filter a location set, keeping only nodes for which the predicate
+ * expression matches. Afterwards, keep only nodes between minPos and maxPos
+ * in the filtered result.
+ */
+static void
+xmlXPathLocationSetFilter(xmlXPathParserContextPtr ctxt,
+ xmlLocationSetPtr locset,
+ int filterOpIndex,
+ int minPos, int maxPos)
+{
+ xmlXPathContextPtr xpctxt;
+ xmlNodePtr oldnode;
+ xmlDocPtr olddoc;
+ xmlXPathStepOpPtr filterOp;
+ int oldcs, oldpp;
+ int i, j, pos;
- /*
- * Save old context.
- */
- oldContextNode = xpctxt->node;
- oldContextDoc = xpctxt->doc;
- oldcs = xpctxt->contextSize;
- oldpp = xpctxt->proximityPosition;
- /*
- * Get the expression of this predicate.
- */
- exprOp = &ctxt->comp->steps[op->ch2];
- for (i = 0; i < set->nodeNr; i++) {
- xmlXPathObjectPtr tmp;
+ if ((locset == NULL) || (locset->locNr == 0) || (filterOpIndex == -1))
+ return;
- if (set->nodeTab[i] == NULL)
- continue;
+ xpctxt = ctxt->context;
+ oldnode = xpctxt->node;
+ olddoc = xpctxt->doc;
+ oldcs = xpctxt->contextSize;
+ oldpp = xpctxt->proximityPosition;
+ filterOp = &ctxt->comp->steps[filterOpIndex];
- contextNode = set->nodeTab[i];
- xpctxt->node = contextNode;
- xpctxt->contextSize = contextSize;
- xpctxt->proximityPosition = ++contextPos;
+ xpctxt->contextSize = locset->locNr;
- /*
- * Initialize the new set.
- * Also set the xpath document in case things like
- * key() evaluation are attempted on the predicate
- */
- if ((contextNode->type != XML_NAMESPACE_DECL) &&
- (contextNode->doc != NULL))
- xpctxt->doc = contextNode->doc;
- /*
- * Evaluate the predicate expression with 1 context node
- * at a time; this node is packaged into a node set; this
- * node set is handed over to the evaluation mechanism.
- */
- if (contextObj == NULL)
- contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode);
- else {
- if (xmlXPathNodeSetAddUnique(contextObj->nodesetval,
- contextNode) < 0) {
- ctxt->error = XPATH_MEMORY_ERROR;
- goto evaluation_exit;
- }
- }
+ for (i = 0, j = 0, pos = 1; i < locset->locNr; i++) {
+ xmlNodePtr contextNode = locset->locTab[i]->user;
+ int res;
- valuePush(ctxt, contextObj);
- frame = xmlXPathSetFrame(ctxt);
- res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1);
- xmlXPathPopFrame(ctxt, frame);
- tmp = valuePop(ctxt);
+ xpctxt->node = contextNode;
+ xpctxt->proximityPosition = i + 1;
- if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) {
- while (tmp != contextObj) {
- /*
- * Free up the result
- * then pop off contextObj, which will be freed later
- */
- xmlXPathReleaseObject(xpctxt, tmp);
- tmp = valuePop(ctxt);
+ /*
+ * Also set the xpath document in case things like
+ * key() are evaluated in the predicate.
+ *
+ * TODO: Get real doc for namespace nodes.
+ */
+ if ((contextNode->type != XML_NAMESPACE_DECL) &&
+ (contextNode->doc != NULL))
+ xpctxt->doc = contextNode->doc;
+
+ res = xmlXPathCompOpEvalToBoolean(ctxt, filterOp, 1);
+
+ if (ctxt->error != XPATH_EXPRESSION_OK)
+ goto exit;
+ if (res < 0) {
+ /* Shouldn't happen */
+ xmlXPathErr(ctxt, XPATH_EXPR_ERROR);
+ goto exit;
+ }
+
+ if ((res != 0) && ((pos >= minPos) && (pos <= maxPos))) {
+ if (i != j) {
+ locset->locTab[j] = locset->locTab[i];
+ locset->locTab[i] = NULL;
+ }
+
+ j += 1;
+ } else {
+ /* Remove the entry from the initial location set. */
+ xmlXPathFreeObject(locset->locTab[i]);
+ locset->locTab[i] = NULL;
+ }
+
+ if (res != 0) {
+ if (pos == maxPos) {
+ /* Clear remaining nodes and exit loop. */
+ for (i++; i < locset->locNr; i++) {
+ xmlXPathFreeObject(locset->locTab[i]);
}
- goto evaluation_error;
- }
- /* push the result back onto the stack */
- valuePush(ctxt, tmp);
+ break;
+ }
- if (res)
- pos++;
+ pos += 1;
+ }
+ }
- if (res && (pos >= minPos) && (pos <= maxPos)) {
- /*
- * Fits in the requested range.
- */
- newContextSize++;
- if (minPos == maxPos) {
- /*
- * Only 1 node was requested.
- */
- if (contextNode->type == XML_NAMESPACE_DECL) {
- /*
- * As always: take care of those nasty
- * namespace nodes.
- */
- set->nodeTab[i] = NULL;
- }
- xmlXPathNodeSetClear(set, hasNsNodes);
- set->nodeNr = 1;
- set->nodeTab[0] = contextNode;
- goto evaluation_exit;
- }
- if (pos == maxPos) {
- /*
- * We are done.
- */
- xmlXPathNodeSetClearFromPos(set, i +1, hasNsNodes);
- goto evaluation_exit;
- }
- } else {
- /*
- * Remove the entry from the initial node set.
- */
- set->nodeTab[i] = NULL;
- if (contextNode->type == XML_NAMESPACE_DECL)
- xmlXPathNodeSetFreeNs((xmlNsPtr) contextNode);
- }
- if (exprRes != NULL) {
- xmlXPathReleaseObject(ctxt->context, exprRes);
- exprRes = NULL;
- }
- if (ctxt->value == contextObj) {
- /*
- * Don't free the temporary XPath object holding the
- * context node, in order to avoid massive recreation
- * inside this loop.
- */
- valuePop(ctxt);
- xmlXPathNodeSetClear(contextObj->nodesetval, hasNsNodes);
- } else {
- /*
- * The object was lost in the evaluation machinery.
- * Can this happen? Maybe in case of internal-errors.
- */
- contextObj = NULL;
- }
- }
- goto evaluation_exit;
+ locset->locNr = j;
-evaluation_error:
- xmlXPathNodeSetClear(set, hasNsNodes);
- newContextSize = 0;
+ /* If too many elements were removed, shrink table to preserve memory. */
+ if ((locset->locMax > XML_NODESET_DEFAULT) &&
+ (locset->locNr < locset->locMax / 2)) {
+ xmlXPathObjectPtr *tmp;
+ int locMax = locset->locNr;
-evaluation_exit:
- if (contextObj != NULL) {
- if (ctxt->value == contextObj)
- valuePop(ctxt);
- xmlXPathReleaseObject(xpctxt, contextObj);
- }
- if (exprRes != NULL)
- xmlXPathReleaseObject(ctxt->context, exprRes);
+ if (locMax < XML_NODESET_DEFAULT)
+ locMax = XML_NODESET_DEFAULT;
+ tmp = (xmlXPathObjectPtr *) xmlRealloc(locset->locTab,
+ locMax * sizeof(xmlXPathObjectPtr));
+ if (tmp == NULL) {
+ xmlXPathPErrMemory(ctxt, "shrinking locset\n");
+ } else {
+ locset->locTab = tmp;
+ locset->locMax = locMax;
+ }
+ }
+
+exit:
+ xpctxt->node = oldnode;
+ xpctxt->doc = olddoc;
+ xpctxt->contextSize = oldcs;
+ xpctxt->proximityPosition = oldpp;
+}
+#endif /* LIBXML_XPTR_ENABLED */
+
+/**
+ * xmlXPathCompOpEvalPredicate:
+ * @ctxt: the XPath Parser context
+ * @op: the predicate op
+ * @set: the node set to filter
+ * @minPos: minimum position in the filtered set (1-based)
+ * @maxPos: maximum position in the filtered set (1-based)
+ * @hasNsNodes: true if the node set may contain namespace nodes
+ *
+ * Filter a node set, keeping only nodes for which the sequence of predicate
+ * expressions matches. Afterwards, keep only nodes between minPos and maxPos
+ * in the filtered result.
+ */
+static void
+xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
+ xmlXPathStepOpPtr op,
+ xmlNodeSetPtr set,
+ int minPos, int maxPos,
+ int hasNsNodes)
+{
+ if (op->ch1 != -1) {
+ xmlXPathCompExprPtr comp = ctxt->comp;
/*
- * Reset/invalidate the context.
+ * Process inner predicates first.
*/
- xpctxt->node = oldContextNode;
- xpctxt->doc = oldContextDoc;
- xpctxt->contextSize = oldcs;
- xpctxt->proximityPosition = oldpp;
- return(newContextSize);
+ if (comp->steps[op->ch1].op != XPATH_OP_PREDICATE) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlXPathCompOpEvalPredicate: Expected a predicate\n");
+ XP_ERROR(XPATH_INVALID_OPERAND);
+ }
+ xmlXPathCompOpEvalPredicate(ctxt, &comp->steps[op->ch1], set,
+ 1, set->nodeNr, hasNsNodes);
+ CHECK_ERROR;
}
- return(contextSize);
+
+ if (op->ch2 != -1)
+ xmlXPathNodeSetFilter(ctxt, set, op->ch2, minPos, maxPos, hasNsNodes);
}
static int
@@ -12131,7 +11976,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
/* First predicate operator */
xmlXPathStepOpPtr predOp;
int maxPos; /* The requested position() (when a "[n]" predicate) */
- int hasPredicateRange, hasAxisRange, pos, size, newSize;
+ int hasPredicateRange, hasAxisRange, pos;
int breakOnFirstHit;
xmlXPathTraversalFunction next = NULL;
@@ -12545,7 +12390,7 @@ axis_range_end: /* ----------------------------------------------------- */
outSeq = seq;
seq = NULL;
} else
- outSeq = mergeAndClear(outSeq, seq, 0);
+ outSeq = mergeAndClear(outSeq, seq);
/*
* Break if only a true/false result was requested.
*/
@@ -12562,7 +12407,7 @@ first_hit: /* ---------------------------------------------------------- */
outSeq = seq;
seq = NULL;
} else
- outSeq = mergeAndClear(outSeq, seq, 0);
+ outSeq = mergeAndClear(outSeq, seq);
break;
#ifdef DEBUG_STEP
@@ -12606,51 +12451,20 @@ apply_predicates: /* --------------------------------------------------- */
* For the moment, I'll try to solve this with a recursive
* function: xmlXPathCompOpEvalPredicate().
*/
- size = seq->nodeNr;
if (hasPredicateRange != 0)
- newSize = xmlXPathCompOpEvalPositionalPredicate(ctxt,
- predOp, seq, size, maxPos, maxPos, hasNsNodes);
+ xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, maxPos, maxPos,
+ hasNsNodes);
else
- newSize = xmlXPathCompOpEvalPredicate(ctxt,
- predOp, seq, size, hasNsNodes);
+ xmlXPathCompOpEvalPredicate(ctxt, predOp, seq, 1, seq->nodeNr,
+ hasNsNodes);
if (ctxt->error != XPATH_EXPRESSION_OK) {
total = 0;
goto error;
}
- /*
- * Add the filtered set of nodes to the result node set.
- */
- if (newSize == 0) {
- /*
- * The predicates filtered all nodes out.
- */
- xmlXPathNodeSetClear(seq, hasNsNodes);
- } else if (seq->nodeNr > 0) {
- /*
- * Add to result set.
- */
- if (outSeq == NULL) {
- if (size != newSize) {
- /*
- * We need to merge and clear here, since
- * the sequence will contained NULLed entries.
- */
- outSeq = mergeAndClear(NULL, seq, 1);
- } else {
- outSeq = seq;
- seq = NULL;
- }
- } else
- outSeq = mergeAndClear(outSeq, seq,
- (size != newSize) ? 1: 0);
- /*
- * Break if only a true/false result was requested.
- */
- if (toBool)
- break;
- }
- } else if (seq->nodeNr > 0) {
+ }
+
+ if (seq->nodeNr > 0) {
/*
* Add to result set.
*/
@@ -12658,8 +12472,11 @@ apply_predicates: /* --------------------------------------------------- */
outSeq = seq;
seq = NULL;
} else {
- outSeq = mergeAndClear(outSeq, seq, 0);
+ outSeq = mergeAndClear(outSeq, seq);
}
+
+ if (toBool)
+ break;
}
}
@@ -13002,13 +12819,7 @@ xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
{
int total = 0;
xmlXPathCompExprPtr comp;
- xmlXPathObjectPtr res;
- xmlXPathObjectPtr obj;
- xmlNodeSetPtr oldset;
- xmlNodePtr oldnode;
- xmlDocPtr oldDoc;
- int oldcs, oldpp;
- int i;
+ xmlNodeSetPtr set;
CHECK_ERROR0;
comp = ctxt->comp;
@@ -13063,205 +12874,27 @@ xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
* Hum are we filtering the result of an XPointer expression
*/
if (ctxt->value->type == XPATH_LOCATIONSET) {
- xmlXPathObjectPtr tmp = NULL;
- xmlLocationSetPtr newlocset = NULL;
- xmlLocationSetPtr oldlocset;
-
- /*
- * Extract the old locset, and then evaluate the result of the
- * expression for all the element in the locset. use it to grow
- * up a new locset.
- */
- CHECK_TYPE0(XPATH_LOCATIONSET);
-
- if ((ctxt->value->user == NULL) ||
- (((xmlLocationSetPtr) ctxt->value->user)->locNr == 0))
- return (total);
+ xmlLocationSetPtr locset = ctxt->value->user;
- obj = valuePop(ctxt);
- oldlocset = obj->user;
- oldnode = ctxt->context->node;
- oldcs = ctxt->context->contextSize;
- oldpp = ctxt->context->proximityPosition;
-
- newlocset = xmlXPtrLocationSetCreate(NULL);
+ if (locset != NULL) {
+ xmlXPathLocationSetFilter(ctxt, locset, op->ch2, 1, 1);
+ if (locset->locNr > 0)
+ *first = (xmlNodePtr) locset->locTab[0]->user;
+ }
- for (i = 0; i < oldlocset->locNr; i++) {
- /*
- * Run the evaluation with a node list made of a
- * single item in the nodelocset.
- */
- ctxt->context->node = oldlocset->locTab[i]->user;
- ctxt->context->contextSize = oldlocset->locNr;
- ctxt->context->proximityPosition = i + 1;
- if (tmp == NULL) {
- tmp = xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node);
- } else {
- if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
- ctxt->context->node) < 0) {
- ctxt->error = XPATH_MEMORY_ERROR;
- }
- }
- valuePush(ctxt, tmp);
- if (op->ch2 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPtrFreeLocationSet(newlocset);
- goto xptr_error;
- }
- /*
- * The result of the evaluation need to be tested to
- * decided whether the filter succeeded or not
- */
- res = valuePop(ctxt);
- if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
- xmlXPtrLocationSetAdd(newlocset,
- xmlXPathCacheObjectCopy(ctxt->context,
- oldlocset->locTab[i]));
- }
- /*
- * Cleanup
- */
- if (res != NULL) {
- xmlXPathReleaseObject(ctxt->context, res);
- }
- if (ctxt->value == tmp) {
- valuePop(ctxt);
- xmlXPathNodeSetClear(tmp->nodesetval, 1);
- /*
- * REVISIT TODO: Don't create a temporary nodeset
- * for everly iteration.
- */
- /* OLD: xmlXPathFreeObject(res); */
- } else
- tmp = NULL;
- /*
- * Only put the first node in the result, then leave.
- */
- if (newlocset->locNr > 0) {
- *first = (xmlNodePtr) oldlocset->locTab[i]->user;
- break;
- }
- }
- if (tmp != NULL) {
- xmlXPathReleaseObject(ctxt->context, tmp);
- }
- /*
- * The result is used as the new evaluation locset.
- */
- valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
-xptr_error:
- xmlXPathReleaseObject(ctxt->context, obj);
- ctxt->context->node = oldnode;
- ctxt->context->contextSize = oldcs;
- ctxt->context->proximityPosition = oldpp;
return (total);
}
#endif /* LIBXML_XPTR_ENABLED */
- /*
- * Extract the old set, and then evaluate the result of the
- * expression for all the element in the set. use it to grow
- * up a new set.
- */
CHECK_TYPE0(XPATH_NODESET);
-
- if ((ctxt->value->nodesetval != NULL) &&
- (ctxt->value->nodesetval->nodeNr != 0)) {
- xmlNodeSetPtr newset;
- xmlXPathObjectPtr tmp = NULL;
-
- obj = valuePop(ctxt);
- oldset = obj->nodesetval;
- oldnode = ctxt->context->node;
- oldDoc = ctxt->context->doc;
- oldcs = ctxt->context->contextSize;
- oldpp = ctxt->context->proximityPosition;
-
- /*
- * Initialize the new set.
- * Also set the xpath document in case things like
- * key() evaluation are attempted on the predicate
- */
- newset = xmlXPathNodeSetCreate(NULL);
- /* XXX what if xmlXPathNodeSetCreate returned NULL? */
-
- for (i = 0; i < oldset->nodeNr; i++) {
- /*
- * Run the evaluation with a node list made of
- * a single item in the nodeset.
- */
- ctxt->context->node = oldset->nodeTab[i];
- if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
- (oldset->nodeTab[i]->doc != NULL))
- ctxt->context->doc = oldset->nodeTab[i]->doc;
- if (tmp == NULL) {
- tmp = xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node);
- } else {
- if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
- ctxt->context->node) < 0) {
- ctxt->error = XPATH_MEMORY_ERROR;
- }
- }
- valuePush(ctxt, tmp);
- ctxt->context->contextSize = oldset->nodeNr;
- ctxt->context->proximityPosition = i + 1;
- if (op->ch2 != -1)
- total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPathFreeNodeSet(newset);
- goto error;
- }
- /*
- * The result of the evaluation needs to be tested to
- * decide whether the filter succeeded or not
- */
- res = valuePop(ctxt);
- if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
- if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]) < 0)
- ctxt->error = XPATH_MEMORY_ERROR;
- }
- /*
- * Cleanup
- */
- if (res != NULL) {
- xmlXPathReleaseObject(ctxt->context, res);
- }
- if (ctxt->value == tmp) {
- valuePop(ctxt);
- /*
- * Don't free the temporary nodeset
- * in order to avoid massive recreation inside this
- * loop.
- */
- xmlXPathNodeSetClear(tmp->nodesetval, 1);
- } else
- tmp = NULL;
- /*
- * Only put the first node in the result, then leave.
- */
- if (newset->nodeNr > 0) {
- *first = *(newset->nodeTab);
- break;
- }
- }
- if (tmp != NULL) {
- xmlXPathReleaseObject(ctxt->context, tmp);
- }
- /*
- * The result is used as the new evaluation set.
- */
- valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, newset));
-error:
- xmlXPathReleaseObject(ctxt->context, obj);
- ctxt->context->node = oldnode;
- ctxt->context->doc = oldDoc;
- ctxt->context->contextSize = oldcs;
- ctxt->context->proximityPosition = oldpp;
+ set = ctxt->value->nodesetval;
+ if (set != NULL) {
+ xmlXPathNodeSetFilter(ctxt, set, op->ch2, 1, 1, 1);
+ if (set->nodeNr > 0)
+ *first = set->nodeTab[0];
}
- return(total);
+
+ return (total);
}
#endif /* XP_OPTIMIZED_FILTER_FIRST */
@@ -13556,14 +13189,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
break;
case XPATH_OP_PREDICATE:
case XPATH_OP_FILTER:{
- xmlXPathObjectPtr res;
- xmlXPathObjectPtr obj, tmp;
- xmlNodeSetPtr newset = NULL;
- xmlNodeSetPtr oldset;
- xmlNodePtr oldnode;
- xmlDocPtr oldDoc;
- int oldcs, oldpp;
- int i;
+ xmlNodeSetPtr set;
/*
* Optimization for ()[1] selection i.e. the first elem
@@ -13670,214 +13296,16 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
* Hum are we filtering the result of an XPointer expression
*/
if (ctxt->value->type == XPATH_LOCATIONSET) {
- xmlLocationSetPtr newlocset = NULL;
- xmlLocationSetPtr oldlocset;
-
- /*
- * Extract the old locset, and then evaluate the result of the
- * expression for all the element in the locset. use it to grow
- * up a new locset.
- */
- CHECK_TYPE0(XPATH_LOCATIONSET);
-
- if ((ctxt->value->user == NULL) ||
- (((xmlLocationSetPtr) ctxt->value->user)->locNr == 0))
- break;
-
- obj = valuePop(ctxt);
- oldlocset = obj->user;
- oldnode = ctxt->context->node;
- oldcs = ctxt->context->contextSize;
- oldpp = ctxt->context->proximityPosition;
-
- newlocset = xmlXPtrLocationSetCreate(NULL);
-
- for (i = 0; i < oldlocset->locNr; i++) {
- /*
- * Run the evaluation with a node list made of a
- * single item in the nodelocset.
- */
- ctxt->context->node = oldlocset->locTab[i]->user;
- ctxt->context->contextSize = oldlocset->locNr;
- ctxt->context->proximityPosition = i + 1;
- tmp = xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node);
- valuePush(ctxt, tmp);
-
- if (op->ch2 != -1)
- total +=
- xmlXPathCompOpEval(ctxt,
- &comp->steps[op->ch2]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPtrFreeLocationSet(newlocset);
- goto filter_xptr_error;
- }
-
- /*
- * The result of the evaluation need to be tested to
- * decided whether the filter succeeded or not
- */
- res = valuePop(ctxt);
- if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
- xmlXPtrLocationSetAdd(newlocset,
- xmlXPathObjectCopy
- (oldlocset->locTab[i]));
- }
-
- /*
- * Cleanup
- */
- if (res != NULL) {
- xmlXPathReleaseObject(ctxt->context, res);
- }
- if (ctxt->value == tmp) {
- res = valuePop(ctxt);
- xmlXPathReleaseObject(ctxt->context, res);
- }
- }
-
- /*
- * The result is used as the new evaluation locset.
- */
- valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
-filter_xptr_error:
- xmlXPathReleaseObject(ctxt->context, obj);
- ctxt->context->node = oldnode;
- ctxt->context->contextSize = oldcs;
- ctxt->context->proximityPosition = oldpp;
+ xmlLocationSetPtr locset = ctxt->value->user;
+ xmlXPathLocationSetFilter(ctxt, locset, op->ch2,
+ 1, locset->locNr);
break;
}
#endif /* LIBXML_XPTR_ENABLED */
- /*
- * Extract the old set, and then evaluate the result of the
- * expression for all the element in the set. use it to grow
- * up a new set.
- */
CHECK_TYPE0(XPATH_NODESET);
-
- if ((ctxt->value->nodesetval != NULL) &&
- (ctxt->value->nodesetval->nodeNr != 0)) {
- obj = valuePop(ctxt);
- oldset = obj->nodesetval;
- oldnode = ctxt->context->node;
- oldDoc = ctxt->context->doc;
- oldcs = ctxt->context->contextSize;
- oldpp = ctxt->context->proximityPosition;
- tmp = NULL;
- /*
- * Initialize the new set.
- * Also set the xpath document in case things like
- * key() evaluation are attempted on the predicate
- */
- newset = xmlXPathNodeSetCreate(NULL);
- /*
- * SPEC XPath 1.0:
- * "For each node in the node-set to be filtered, the
- * PredicateExpr is evaluated with that node as the
- * context node, with the number of nodes in the
- * node-set as the context size, and with the proximity
- * position of the node in the node-set with respect to
- * the axis as the context position;"
- * @oldset is the node-set" to be filtered.
- *
- * SPEC XPath 1.0:
- * "only predicates change the context position and
- * context size (see [2.4 Predicates])."
- * Example:
- * node-set context pos
- * nA 1
- * nB 2
- * nC 3
- * After applying predicate [position() > 1] :
- * node-set context pos
- * nB 1
- * nC 2
- *
- * removed the first node in the node-set, then
- * the context position of the
- */
- for (i = 0; i < oldset->nodeNr; i++) {
- /*
- * Run the evaluation with a node list made of
- * a single item in the nodeset.
- */
- ctxt->context->node = oldset->nodeTab[i];
- if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
- (oldset->nodeTab[i]->doc != NULL))
- ctxt->context->doc = oldset->nodeTab[i]->doc;
- if (tmp == NULL) {
- tmp = xmlXPathCacheNewNodeSet(ctxt->context,
- ctxt->context->node);
- } else {
- if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
- ctxt->context->node) < 0) {
- ctxt->error = XPATH_MEMORY_ERROR;
- }
- }
- valuePush(ctxt, tmp);
- ctxt->context->contextSize = oldset->nodeNr;
- ctxt->context->proximityPosition = i + 1;
- /*
- * Evaluate the predicate against the context node.
- * Can/should we optimize position() predicates
- * here (e.g. "[1]")?
- */
- if (op->ch2 != -1)
- total +=
- xmlXPathCompOpEval(ctxt,
- &comp->steps[op->ch2]);
- if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPathFreeNodeSet(newset);
- goto filter_error;
- }
-
- /*
- * The result of the evaluation needs to be tested to
- * decide whether the filter succeeded or not
- */
- /*
- * OPTIMIZE TODO: Can we use
- * xmlXPathNodeSetAdd*Unique()* instead?
- */
- res = valuePop(ctxt);
- if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
- if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i])
- < 0)
- ctxt->error = XPATH_MEMORY_ERROR;
- }
-
- /*
- * Cleanup
- */
- if (res != NULL) {
- xmlXPathReleaseObject(ctxt->context, res);
- }
- if (ctxt->value == tmp) {
- valuePop(ctxt);
- xmlXPathNodeSetClear(tmp->nodesetval, 1);
- /*
- * Don't free the temporary nodeset
- * in order to avoid massive recreation inside this
- * loop.
- */
- } else
- tmp = NULL;
- }
- if (tmp != NULL)
- xmlXPathReleaseObject(ctxt->context, tmp);
- /*
- * The result is used as the new evaluation set.
- */
- valuePush(ctxt,
- xmlXPathCacheWrapNodeSet(ctxt->context, newset));
-filter_error:
- xmlXPathReleaseObject(ctxt->context, obj);
- ctxt->context->node = oldnode;
- ctxt->context->doc = oldDoc;
- ctxt->context->contextSize = oldcs;
- ctxt->context->proximityPosition = oldpp;
- }
+ set = ctxt->value->nodesetval;
+ xmlXPathNodeSetFilter(ctxt, set, op->ch2, 1, set->nodeNr, 1);
break;
}
case XPATH_OP_SORT:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]