diff options
Diffstat (limited to 'xpath.c')
-rw-r--r-- | xpath.c | 3512 |
1 files changed, 3093 insertions, 419 deletions
@@ -2,7 +2,7 @@ * xpath.c: XML Path Language implementation * XPath is a language for addressing parts of an XML document, * designed to be used by both XSLT and XPointer - * + *f * Reference: W3C Recommendation 16 November 1999 * http://www.w3.org/TR/1999/REC-xpath-19991116 * Public reference: @@ -75,6 +75,32 @@ * only element-nodes and the document node. */ #define XP_PATTERN_TO_ANY_NODE_ENABLED + +/* +* XP_OPTIMIZED_NON_ELEM_COMPARISON: +* If defined, this will use xmlXPathCmpNodesExt() instead of +* xmlXPathCmpNodes(). The new function is optimized comparison of +* non-element nodes; actually it will speed up comparison only if +* xmlXPathOrderDocElems() was called in order to index the elements of +* a tree in document order; Libxslt does such an indexing, thus it will +* benefit from this optimization. +*/ +#define XP_OPTIMIZED_NON_ELEM_COMPARISON + +/* +* XP_OPTIMIZED_FILTER_FIRST: +* If defined, this will optimize expressions like "key('foo', 'val')[b][1]" +* in a way, that it stop evaluation at the first node. +*/ +#define XP_OPTIMIZED_FILTER_FIRST + +/* +* XP_DEBUG_OBJ_USAGE: +* Internal flag to enable tracking of how much XPath objects have been +* created. +*/ +/* #define XP_DEBUG_OBJ_USAGE */ + /* * TODO: * There are a few spots where some tests are done which depend upon ascii @@ -85,6 +111,16 @@ #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) /************************************************************************ * * + * Forward declarations * + * * + ************************************************************************/ +static void +xmlXPathFreeValueTree(xmlNodeSetPtr obj); +static void +xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj); + +/************************************************************************ + * * * Floating point stuff * * * ************************************************************************/ @@ -332,6 +368,10 @@ xmlXPathErr(xmlXPathParserContextPtr ctxt, int error) xmlXPathErrorMessages[error]); return; } + + /* cleanup current last error */ + xmlResetError(&ctxt->context->lastError); + ctxt->context->lastError.domain = XML_FROM_XPATH; ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK; @@ -369,6 +409,101 @@ xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED, xmlXPathErr(ctxt, no); } +/************************************************************************ + * * + * Utilities * + * * + ************************************************************************/ + +/** + * xsltPointerList: + * + * Pointer-list for various purposes. + */ +typedef struct _xmlPointerList xmlPointerList; +typedef xmlPointerList *xmlPointerListPtr; +struct _xmlPointerList { + void **items; + int number; + int size; +}; +/* +* TODO: Since such a list-handling is used in xmlschemas.c and libxslt +* and here, we should make the functions public. +*/ +static int +xmlPointerListAddSize(xmlPointerListPtr list, + void *item, + int initialSize) +{ + if (list->items == NULL) { + if (initialSize <= 0) + initialSize = 1; + list->items = (void **) xmlMalloc( + initialSize * sizeof(void *)); + if (list->items == NULL) { + xmlXPathErrMemory(NULL, + "xmlPointerListCreate: allocating item\n"); + return(-1); + } + list->number = 0; + list->size = initialSize; + } else if (list->size <= list->number) { + list->size *= 2; + list->items = (void **) xmlRealloc(list->items, + list->size * sizeof(void *)); + if (list->items == NULL) { + xmlXPathErrMemory(NULL, + "xmlPointerListCreate: re-allocating item\n"); + list->size = 0; + return(-1); + } + } + list->items[list->number++] = item; + return(0); +} + +/** + * xsltPointerListCreate: + * + * Creates an xsltPointerList structure. + * + * Returns a xsltPointerList structure or NULL in case of an error. + */ +static xmlPointerListPtr +xmlPointerListCreate(int initialSize) +{ + xmlPointerListPtr ret; + + ret = xmlMalloc(sizeof(xmlPointerList)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, + "xmlPointerListCreate: allocating item\n"); + return (NULL); + } + memset(ret, 0, sizeof(xmlPointerList)); + if (initialSize > 0) { + xmlPointerListAddSize(ret, NULL, initialSize); + ret->number = 0; + } + return (ret); +} + +/** + * xsltPointerListFree: + * + * Frees the xsltPointerList structure. This does not free + * the content of the list. + */ +static void +xmlPointerListFree(xmlPointerListPtr list) +{ + if (list == NULL) + return; + if (list->items != NULL) + xmlFree(list->items); + xmlFree(list); +} /************************************************************************ * * @@ -391,15 +526,15 @@ typedef enum { XPATH_OP_UNION, XPATH_OP_ROOT, XPATH_OP_NODE, - XPATH_OP_RESET, + XPATH_OP_RESET, /* 10 */ XPATH_OP_COLLECT, - XPATH_OP_VALUE, + XPATH_OP_VALUE, /* 12 */ XPATH_OP_VARIABLE, XPATH_OP_FUNCTION, XPATH_OP_ARG, XPATH_OP_PREDICATE, - XPATH_OP_FILTER, - XPATH_OP_SORT + XPATH_OP_FILTER, /* 17 */ + XPATH_OP_SORT /* 18 */ #ifdef LIBXML_XPTR_ENABLED ,XPATH_OP_RANGETO #endif @@ -418,7 +553,7 @@ typedef enum { AXIS_PARENT, AXIS_PRECEDING, AXIS_PRECEDING_SIBLING, - AXIS_SELF + AXIS_SELF } xmlXPathAxisVal; typedef enum { @@ -434,9 +569,10 @@ typedef enum { NODE_TYPE_NODE = 0, NODE_TYPE_COMMENT = XML_COMMENT_NODE, NODE_TYPE_TEXT = XML_TEXT_NODE, - NODE_TYPE_PI = XML_PI_NODE + NODE_TYPE_PI = XML_PI_NODE } xmlXPathTypeVal; +#define XP_REWRITE_DOS_CHILD_ELEM 1 typedef struct _xmlXPathStepOp xmlXPathStepOp; typedef xmlXPathStepOp *xmlXPathStepOpPtr; @@ -451,6 +587,7 @@ struct _xmlXPathStepOp { void *value5; void *cache; void *cacheURI; + int rewriteType; }; struct _xmlXPathCompExpr { @@ -673,6 +810,58 @@ xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \ /************************************************************************ * * + * XPath object cache structures * + * * + ************************************************************************/ + +/* #define XP_DEFAULT_CACHE_ON */ + +#define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL)) + +typedef struct _xmlXPathContextCache xmlXPathContextCache; +typedef xmlXPathContextCache *xmlXPathContextCachePtr; +struct _xmlXPathContextCache { + xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */ + int maxNodeset; + int maxString; + int maxBoolean; + int maxNumber; + int maxMisc; +#ifdef XP_DEBUG_OBJ_USAGE + int dbgCachedAll; + int dbgCachedNodeset; + int dbgCachedString; + int dbgCachedBool; + int dbgCachedNumber; + int dbgCachedPoint; + int dbgCachedRange; + int dbgCachedLocset; + int dbgCachedUsers; + int dbgCachedXSLTTree; + int dbgCachedUndefined; + + + int dbgReusedAll; + int dbgReusedNodeset; + int dbgReusedString; + int dbgReusedBool; + int dbgReusedNumber; + int dbgReusedPoint; + int dbgReusedRange; + int dbgReusedLocset; + int dbgReusedUsers; + int dbgReusedXSLTTree; + int dbgReusedUndefined; + +#endif +}; + +/************************************************************************ + * * * Debugging related functions * * * ************************************************************************/ @@ -1118,10 +1307,1096 @@ xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp, i = comp->last; xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1); } + +#ifdef XP_DEBUG_OBJ_USAGE + +/* +* XPath object usage related debugging variables. +*/ +static int xmlXPathDebugObjCounterUndefined = 0; +static int xmlXPathDebugObjCounterNodeset = 0; +static int xmlXPathDebugObjCounterBool = 0; +static int xmlXPathDebugObjCounterNumber = 0; +static int xmlXPathDebugObjCounterString = 0; +static int xmlXPathDebugObjCounterPoint = 0; +static int xmlXPathDebugObjCounterRange = 0; +static int xmlXPathDebugObjCounterLocset = 0; +static int xmlXPathDebugObjCounterUsers = 0; +static int xmlXPathDebugObjCounterXSLTTree = 0; +static int xmlXPathDebugObjCounterAll = 0; + +static int xmlXPathDebugObjTotalUndefined = 0; +static int xmlXPathDebugObjTotalNodeset = 0; +static int xmlXPathDebugObjTotalBool = 0; +static int xmlXPathDebugObjTotalNumber = 0; +static int xmlXPathDebugObjTotalString = 0; +static int xmlXPathDebugObjTotalPoint = 0; +static int xmlXPathDebugObjTotalRange = 0; +static int xmlXPathDebugObjTotalLocset = 0; +static int xmlXPathDebugObjTotalUsers = 0; +static int xmlXPathDebugObjTotalXSLTTree = 0; +static int xmlXPathDebugObjTotalAll = 0; + +static int xmlXPathDebugObjMaxUndefined = 0; +static int xmlXPathDebugObjMaxNodeset = 0; +static int xmlXPathDebugObjMaxBool = 0; +static int xmlXPathDebugObjMaxNumber = 0; +static int xmlXPathDebugObjMaxString = 0; +static int xmlXPathDebugObjMaxPoint = 0; +static int xmlXPathDebugObjMaxRange = 0; +static int xmlXPathDebugObjMaxLocset = 0; +static int xmlXPathDebugObjMaxUsers = 0; +static int xmlXPathDebugObjMaxXSLTTree = 0; +static int xmlXPathDebugObjMaxAll = 0; + +/* REVISIT TODO: Make this static when committing */ +static void +xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt) +{ + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + cache->dbgCachedAll = 0; + cache->dbgCachedNodeset = 0; + cache->dbgCachedString = 0; + cache->dbgCachedBool = 0; + cache->dbgCachedNumber = 0; + cache->dbgCachedPoint = 0; + cache->dbgCachedRange = 0; + cache->dbgCachedLocset = 0; + cache->dbgCachedUsers = 0; + cache->dbgCachedXSLTTree = 0; + cache->dbgCachedUndefined = 0; + + cache->dbgReusedAll = 0; + cache->dbgReusedNodeset = 0; + cache->dbgReusedString = 0; + cache->dbgReusedBool = 0; + cache->dbgReusedNumber = 0; + cache->dbgReusedPoint = 0; + cache->dbgReusedRange = 0; + cache->dbgReusedLocset = 0; + cache->dbgReusedUsers = 0; + cache->dbgReusedXSLTTree = 0; + cache->dbgReusedUndefined = 0; + } + } + + xmlXPathDebugObjCounterUndefined = 0; + xmlXPathDebugObjCounterNodeset = 0; + xmlXPathDebugObjCounterBool = 0; + xmlXPathDebugObjCounterNumber = 0; + xmlXPathDebugObjCounterString = 0; + xmlXPathDebugObjCounterPoint = 0; + xmlXPathDebugObjCounterRange = 0; + xmlXPathDebugObjCounterLocset = 0; + xmlXPathDebugObjCounterUsers = 0; + xmlXPathDebugObjCounterXSLTTree = 0; + xmlXPathDebugObjCounterAll = 0; + + xmlXPathDebugObjTotalUndefined = 0; + xmlXPathDebugObjTotalNodeset = 0; + xmlXPathDebugObjTotalBool = 0; + xmlXPathDebugObjTotalNumber = 0; + xmlXPathDebugObjTotalString = 0; + xmlXPathDebugObjTotalPoint = 0; + xmlXPathDebugObjTotalRange = 0; + xmlXPathDebugObjTotalLocset = 0; + xmlXPathDebugObjTotalUsers = 0; + xmlXPathDebugObjTotalXSLTTree = 0; + xmlXPathDebugObjTotalAll = 0; + + xmlXPathDebugObjMaxUndefined = 0; + xmlXPathDebugObjMaxNodeset = 0; + xmlXPathDebugObjMaxBool = 0; + xmlXPathDebugObjMaxNumber = 0; + xmlXPathDebugObjMaxString = 0; + xmlXPathDebugObjMaxPoint = 0; + xmlXPathDebugObjMaxRange = 0; + xmlXPathDebugObjMaxLocset = 0; + xmlXPathDebugObjMaxUsers = 0; + xmlXPathDebugObjMaxXSLTTree = 0; + xmlXPathDebugObjMaxAll = 0; + +} + +static void +xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt, + xmlXPathObjectType objType) +{ + int isCached = 0; + + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + isCached = 1; + + cache->dbgReusedAll++; + switch (objType) { + case XPATH_UNDEFINED: + cache->dbgReusedUndefined++; + break; + case XPATH_NODESET: + cache->dbgReusedNodeset++; + break; + case XPATH_BOOLEAN: + cache->dbgReusedBool++; + break; + case XPATH_NUMBER: + cache->dbgReusedNumber++; + break; + case XPATH_STRING: + cache->dbgReusedString++; + break; + case XPATH_POINT: + cache->dbgReusedPoint++; + break; + case XPATH_RANGE: + cache->dbgReusedRange++; + break; + case XPATH_LOCATIONSET: + cache->dbgReusedLocset++; + break; + case XPATH_USERS: + cache->dbgReusedUsers++; + break; + case XPATH_XSLT_TREE: + cache->dbgReusedXSLTTree++; + break; + default: + break; + } + } + } + + switch (objType) { + case XPATH_UNDEFINED: + if (! isCached) + xmlXPathDebugObjTotalUndefined++; + xmlXPathDebugObjCounterUndefined++; + if (xmlXPathDebugObjCounterUndefined > + xmlXPathDebugObjMaxUndefined) + xmlXPathDebugObjMaxUndefined = + xmlXPathDebugObjCounterUndefined; + break; + case XPATH_NODESET: + if (! isCached) + xmlXPathDebugObjTotalNodeset++; + xmlXPathDebugObjCounterNodeset++; + if (xmlXPathDebugObjCounterNodeset > + xmlXPathDebugObjMaxNodeset) + xmlXPathDebugObjMaxNodeset = + xmlXPathDebugObjCounterNodeset; + break; + case XPATH_BOOLEAN: + if (! isCached) + xmlXPathDebugObjTotalBool++; + xmlXPathDebugObjCounterBool++; + if (xmlXPathDebugObjCounterBool > + xmlXPathDebugObjMaxBool) + xmlXPathDebugObjMaxBool = + xmlXPathDebugObjCounterBool; + break; + case XPATH_NUMBER: + if (! isCached) + xmlXPathDebugObjTotalNumber++; + xmlXPathDebugObjCounterNumber++; + if (xmlXPathDebugObjCounterNumber > + xmlXPathDebugObjMaxNumber) + xmlXPathDebugObjMaxNumber = + xmlXPathDebugObjCounterNumber; + break; + case XPATH_STRING: + if (! isCached) + xmlXPathDebugObjTotalString++; + xmlXPathDebugObjCounterString++; + if (xmlXPathDebugObjCounterString > + xmlXPathDebugObjMaxString) + xmlXPathDebugObjMaxString = + xmlXPathDebugObjCounterString; + break; + case XPATH_POINT: + if (! isCached) + xmlXPathDebugObjTotalPoint++; + xmlXPathDebugObjCounterPoint++; + if (xmlXPathDebugObjCounterPoint > + xmlXPathDebugObjMaxPoint) + xmlXPathDebugObjMaxPoint = + xmlXPathDebugObjCounterPoint; + break; + case XPATH_RANGE: + if (! isCached) + xmlXPathDebugObjTotalRange++; + xmlXPathDebugObjCounterRange++; + if (xmlXPathDebugObjCounterRange > + xmlXPathDebugObjMaxRange) + xmlXPathDebugObjMaxRange = + xmlXPathDebugObjCounterRange; + break; + case XPATH_LOCATIONSET: + if (! isCached) + xmlXPathDebugObjTotalLocset++; + xmlXPathDebugObjCounterLocset++; + if (xmlXPathDebugObjCounterLocset > + xmlXPathDebugObjMaxLocset) + xmlXPathDebugObjMaxLocset = + xmlXPathDebugObjCounterLocset; + break; + case XPATH_USERS: + if (! isCached) + xmlXPathDebugObjTotalUsers++; + xmlXPathDebugObjCounterUsers++; + if (xmlXPathDebugObjCounterUsers > + xmlXPathDebugObjMaxUsers) + xmlXPathDebugObjMaxUsers = + xmlXPathDebugObjCounterUsers; + break; + case XPATH_XSLT_TREE: + if (! isCached) + xmlXPathDebugObjTotalXSLTTree++; + xmlXPathDebugObjCounterXSLTTree++; + if (xmlXPathDebugObjCounterXSLTTree > + xmlXPathDebugObjMaxXSLTTree) + xmlXPathDebugObjMaxXSLTTree = + xmlXPathDebugObjCounterXSLTTree; + break; + default: + break; + } + if (! isCached) + xmlXPathDebugObjTotalAll++; + xmlXPathDebugObjCounterAll++; + if (xmlXPathDebugObjCounterAll > + xmlXPathDebugObjMaxAll) + xmlXPathDebugObjMaxAll = + xmlXPathDebugObjCounterAll; +} + +static void +xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt, + xmlXPathObjectType objType) +{ + int isCached = 0; + + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + isCached = 1; + + cache->dbgCachedAll++; + switch (objType) { + case XPATH_UNDEFINED: + cache->dbgCachedUndefined++; + break; + case XPATH_NODESET: + cache->dbgCachedNodeset++; + break; + case XPATH_BOOLEAN: + cache->dbgCachedBool++; + break; + case XPATH_NUMBER: + cache->dbgCachedNumber++; + break; + case XPATH_STRING: + cache->dbgCachedString++; + break; + case XPATH_POINT: + cache->dbgCachedPoint++; + break; + case XPATH_RANGE: + cache->dbgCachedRange++; + break; + case XPATH_LOCATIONSET: + cache->dbgCachedLocset++; + break; + case XPATH_USERS: + cache->dbgCachedUsers++; + break; + case XPATH_XSLT_TREE: + cache->dbgCachedXSLTTree++; + break; + default: + break; + } + + } + } + switch (objType) { + case XPATH_UNDEFINED: + xmlXPathDebugObjCounterUndefined--; + break; + case XPATH_NODESET: + xmlXPathDebugObjCounterNodeset--; + break; + case XPATH_BOOLEAN: + xmlXPathDebugObjCounterBool--; + break; + case XPATH_NUMBER: + xmlXPathDebugObjCounterNumber--; + break; + case XPATH_STRING: + xmlXPathDebugObjCounterString--; + break; + case XPATH_POINT: + xmlXPathDebugObjCounterPoint--; + break; + case XPATH_RANGE: + xmlXPathDebugObjCounterRange--; + break; + case XPATH_LOCATIONSET: + xmlXPathDebugObjCounterLocset--; + break; + case XPATH_USERS: + xmlXPathDebugObjCounterUsers--; + break; + case XPATH_XSLT_TREE: + xmlXPathDebugObjCounterXSLTTree--; + break; + default: + break; + } + xmlXPathDebugObjCounterAll--; +} + +/* REVISIT TODO: Make this static when committing */ +static void +xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt) +{ + int reqAll, reqNodeset, reqString, reqBool, reqNumber, + reqXSLTTree, reqUndefined; + int caAll = 0, caNodeset = 0, caString = 0, caBool = 0, + caNumber = 0, caXSLTTree = 0, caUndefined = 0; + int reAll = 0, reNodeset = 0, reString = 0, reBool = 0, + reNumber = 0, reXSLTTree = 0, reUndefined = 0; + int leftObjs = xmlXPathDebugObjCounterAll; + + reqAll = xmlXPathDebugObjTotalAll; + reqNodeset = xmlXPathDebugObjTotalNodeset; + reqString = xmlXPathDebugObjTotalString; + reqBool = xmlXPathDebugObjTotalBool; + reqNumber = xmlXPathDebugObjTotalNumber; + reqXSLTTree = xmlXPathDebugObjTotalXSLTTree; + reqUndefined = xmlXPathDebugObjTotalUndefined; + + printf("# XPath object usage:\n"); + + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + reAll = cache->dbgReusedAll; + reqAll += reAll; + reNodeset = cache->dbgReusedNodeset; + reqNodeset += reNodeset; + reString = cache->dbgReusedString; + reqString += reString; + reBool = cache->dbgReusedBool; + reqBool += reBool; + reNumber = cache->dbgReusedNumber; + reqNumber += reNumber; + reXSLTTree = cache->dbgReusedXSLTTree; + reqXSLTTree += reXSLTTree; + reUndefined = cache->dbgReusedUndefined; + reqUndefined += reUndefined; + + caAll = cache->dbgCachedAll; + caBool = cache->dbgCachedBool; + caNodeset = cache->dbgCachedNodeset; + caString = cache->dbgCachedString; + caNumber = cache->dbgCachedNumber; + caXSLTTree = cache->dbgCachedXSLTTree; + caUndefined = cache->dbgCachedUndefined; + + if (cache->nodesetObjs) + leftObjs -= cache->nodesetObjs->number; + if (cache->stringObjs) + leftObjs -= cache->stringObjs->number; + if (cache->booleanObjs) + leftObjs -= cache->booleanObjs->number; + if (cache->numberObjs) + leftObjs -= cache->numberObjs->number; + if (cache->miscObjs) + leftObjs -= cache->miscObjs->number; + } + } + + printf("# all\n"); + printf("# total : %d\n", reqAll); + printf("# left : %d\n", leftObjs); + printf("# created: %d\n", xmlXPathDebugObjTotalAll); + printf("# reused : %d\n", reAll); + printf("# max : %d\n", xmlXPathDebugObjMaxAll); + + printf("# node-sets\n"); + printf("# total : %d\n", reqNodeset); + printf("# created: %d\n", xmlXPathDebugObjTotalNodeset); + printf("# reused : %d\n", reNodeset); + printf("# max : %d\n", xmlXPathDebugObjMaxNodeset); + + printf("# strings\n"); + printf("# total : %d\n", reqString); + printf("# created: %d\n", xmlXPathDebugObjTotalString); + printf("# reused : %d\n", reString); + printf("# max : %d\n", xmlXPathDebugObjMaxString); + + printf("# booleans\n"); + printf("# total : %d\n", reqBool); + printf("# created: %d\n", xmlXPathDebugObjTotalBool); + printf("# reused : %d\n", reBool); + printf("# max : %d\n", xmlXPathDebugObjMaxBool); + + printf("# numbers\n"); + printf("# total : %d\n", reqNumber); + printf("# created: %d\n", xmlXPathDebugObjTotalNumber); + printf("# reused : %d\n", reNumber); + printf("# max : %d\n", xmlXPathDebugObjMaxNumber); + + printf("# XSLT result tree fragments\n"); + printf("# total : %d\n", reqXSLTTree); + printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree); + printf("# reused : %d\n", reXSLTTree); + printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree); + + printf("# undefined\n"); + printf("# total : %d\n", reqUndefined); + printf("# created: %d\n", xmlXPathDebugObjTotalUndefined); + printf("# reused : %d\n", reUndefined); + printf("# max : %d\n", xmlXPathDebugObjMaxUndefined); + +} + +#endif /* XP_DEBUG_OBJ_USAGE */ + #endif /* LIBXML_DEBUG_ENABLED */ /************************************************************************ * * + * XPath object caching * + * * + ************************************************************************/ + +/** + * xmlXPathNewCache: + * + * Create a new object cache + * + * Returns the xmlXPathCache just allocated. + */ +static xmlXPathContextCachePtr +xmlXPathNewCache(void) +{ + xmlXPathContextCachePtr ret; + + ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating object cache\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache)); + ret->maxNodeset = 100; + ret->maxString = 100; + ret->maxBoolean = 100; + ret->maxNumber = 100; + ret->maxMisc = 100; + return(ret); +} + +static void +xmlXPathCacheFreeObjectList(xmlPointerListPtr list) +{ + int i; + xmlXPathObjectPtr obj; + + if (list == NULL) + return; + + for (i = 0; i < list->number; i++) { + obj = list->items[i]; + /* + * Note that it is already assured that we don't need to + * look out for namespace nodes in the node-set. + */ + if (obj->nodesetval != NULL) { + if (obj->nodesetval->nodeTab != NULL) + xmlFree(obj->nodesetval->nodeTab); + xmlFree(obj->nodesetval); + } + xmlFree(obj); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjCounterAll--; +#endif + } + xmlPointerListFree(list); +} + +static void +xmlXPathFreeCache(xmlXPathContextCachePtr cache) +{ + if (cache == NULL) + return; + if (cache->nodesetObjs) + xmlXPathCacheFreeObjectList(cache->nodesetObjs); + if (cache->stringObjs) + xmlXPathCacheFreeObjectList(cache->stringObjs); + if (cache->booleanObjs) + xmlXPathCacheFreeObjectList(cache->booleanObjs); + if (cache->numberObjs) + xmlXPathCacheFreeObjectList(cache->numberObjs); + if (cache->miscObjs) + xmlXPathCacheFreeObjectList(cache->miscObjs); + xmlFree(cache); +} + +/** + * xmlXPathContextSetCache: + * + * @ctxt: the XPath context + * @active: enables/disables (creates/frees) the cache + * @value: a value with semantics dependant on @options + * @options: options (currently only the value 0 is used) + * + * Creates/frees an object cache on the XPath context. + * If activates XPath objects (xmlXPathObject) will be cached internally + * to be reused. + * @options: + * 0: This will set the XPath object caching: + * @value: + * This will set the maximum number of XPath objects + * to be cached per slot + * There are 5 slots for: node-set, string, number, boolean, and + * misc objects. Use <0 for the default number (100). + * Other values for @options have currently no effect. + * + * Returns 0 if the setting succeeded, and -1 on API or internal errors. + */ +int +xmlXPathContextSetCache(xmlXPathContextPtr ctxt, + int active, + int value, + int options) +{ + if (ctxt == NULL) + return(-1); + if (active) { + xmlXPathContextCachePtr cache; + + if (ctxt->cache == NULL) { + ctxt->cache = xmlXPathNewCache(); + if (ctxt->cache == NULL) + return(-1); + } + cache = (xmlXPathContextCachePtr) ctxt->cache; + if (options == 0) { + if (value < 0) + value = 100; + cache->maxNodeset = value; + cache->maxString = value; + cache->maxNumber = value; + cache->maxBoolean = value; + cache->maxMisc = value; + } + } else if (ctxt->cache != NULL) { + xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache); + ctxt->cache = NULL; + } + return(0); +} + +/** + * xmlXPathCacheWrapNodeSet: + * @ctxt: the XPath context + * @val: the NodePtr value + * + * This is the cached version of xmlXPathWrapNodeSet(). + * Wrap the Nodeset @val in a new xmlXPathObjectPtr + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val) +{ + if ((ctxt != NULL) && (ctxt->cache != NULL)) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + ret->type = XPATH_NODESET; + ret->nodesetval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); +#endif + return(ret); + } + } + + return(xmlXPathWrapNodeSet(val)); + +} + +/** + * xmlXPathCacheWrapString: + * @ctxt: the XPath context + * @val: the xmlChar * value + * + * This is the cached version of xmlXPathWrapString(). + * Wraps the @val string into an XPath object. + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val) +{ + if ((ctxt != NULL) && (ctxt->cache != NULL)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->stringObjs != NULL) && + (cache->stringObjs->number != 0)) + { + + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->stringObjs->items[--cache->stringObjs->number]; + ret->type = XPATH_STRING; + ret->stringval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + /* + * Fallback to misc-cache. + */ + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_STRING; + ret->stringval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } + } + return(xmlXPathWrapString(val)); +} + +/** + * xmlXPathCacheNewNodeSet: + * @ctxt: the XPath context + * @val: the NodePtr value + * + * This is the cached version of xmlXPathNewNodeSet(). + * Acquire an xmlXPathObjectPtr of type NodeSet and initialize + * it with the single Node @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->nodesetObjs != NULL) && + (cache->nodesetObjs->number != 0)) + { + xmlXPathObjectPtr ret; + /* + * Use the nodset-cache. + */ + ret = (xmlXPathObjectPtr) + cache->nodesetObjs->items[--cache->nodesetObjs->number]; + ret->type = XPATH_NODESET; + ret->boolval = 0; + if (val) { + if ((ret->nodesetval->nodeMax == 0) || + (val->type == XML_NAMESPACE_DECL)) + { + xmlXPathNodeSetAddUnique(ret->nodesetval, val); + } else { + ret->nodesetval->nodeTab[0] = val; + ret->nodesetval->nodeNr = 1; + } + } +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + /* + * Fallback to misc-cache. + */ + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_NODESET; + ret->boolval = 0; + ret->nodesetval = xmlXPathNodeSetCreate(val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); +#endif + return(ret); + } + } + return(xmlXPathNewNodeSet(val)); +} + +/** + * xmlXPathCacheNewCString: + * @ctxt: the XPath context + * @val: the char * value + * + * This is the cached version of xmlXPathNewCString(). + * Acquire an xmlXPathObjectPtr of type string and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->stringObjs != NULL) && + (cache->stringObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->stringObjs->items[--cache->stringObjs->number]; + + ret->type = XPATH_STRING; + ret->stringval = xmlStrdup(BAD_CAST val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_STRING; + ret->stringval = xmlStrdup(BAD_CAST val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } + } + return(xmlXPathNewCString(val)); +} + +/** + * xmlXPathCacheNewString: + * @ctxt: the XPath context + * @val: the xmlChar * value + * + * This is the cached version of xmlXPathNewString(). + * Acquire an xmlXPathObjectPtr of type string and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->stringObjs != NULL) && + (cache->stringObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->stringObjs->items[--cache->stringObjs->number]; + ret->type = XPATH_STRING; + if (val != NULL) + ret->stringval = xmlStrdup(val); + else + ret->stringval = xmlStrdup((const xmlChar *)""); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_STRING; + if (val != NULL) + ret->stringval = xmlStrdup(val); + else + ret->stringval = xmlStrdup((const xmlChar *)""); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } + } + return(xmlXPathNewString(val)); +} + +/** + * xmlXPathCacheNewBoolean: + * @ctxt: the XPath context + * @val: the boolean value + * + * This is the cached version of xmlXPathNewBoolean(). + * Acquires an xmlXPathObjectPtr of type boolean and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->booleanObjs != NULL) && + (cache->booleanObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->booleanObjs->items[--cache->booleanObjs->number]; + ret->type = XPATH_BOOLEAN; + ret->boolval = (val != 0); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_BOOLEAN; + ret->boolval = (val != 0); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN); +#endif + return(ret); + } + } + return(xmlXPathNewBoolean(val)); +} + +/** + * xmlXPathCacheNewFloat: + * @ctxt: the XPath context + * @val: the double value + * + * This is the cached version of xmlXPathNewFloat(). + * Acquires an xmlXPathObjectPtr of type double and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->numberObjs != NULL) && + (cache->numberObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->numberObjs->items[--cache->numberObjs->number]; + ret->type = XPATH_NUMBER; + ret->floatval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_NUMBER; + ret->floatval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER); +#endif + return(ret); + } + } + return(xmlXPathNewFloat(val)); +} + +/** + * xmlXPathCacheConvertString: + * @ctxt: the XPath context + * @val: an XPath object + * + * This is the cached version of xmlXPathConvertString(). + * Converts an existing object to its string() equivalent + * + * Returns a created or reused object, the old one is freed (cached) + * (or the operation is done directly on @val) + */ + +static xmlXPathObjectPtr +xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { + xmlChar *res = NULL; + + if (val == NULL) + return(xmlXPathCacheNewCString(ctxt, "")); + + switch (val->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n"); +#endif + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + res = xmlXPathCastNodeSetToString(val->nodesetval); + break; + case XPATH_STRING: + return(val); + case XPATH_BOOLEAN: + res = xmlXPathCastBooleanToString(val->boolval); + break; + case XPATH_NUMBER: + res = xmlXPathCastNumberToString(val->floatval); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO; + break; + } + xmlXPathReleaseObject(ctxt, val); + if (res == NULL) + return(xmlXPathCacheNewCString(ctxt, "")); + return(xmlXPathCacheWrapString(ctxt, res)); +} + +/** + * xmlXPathCacheObjectCopy: + * @ctxt: the XPath context + * @val: the original object + * + * This is the cached version of xmlXPathObjectCopy(). + * Acquire a copy of a given object + * + * Returns a created or reused created object. + */ +static xmlXPathObjectPtr +xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) +{ + if (val == NULL) + return(NULL); + + switch (val->type) { + case XPATH_NODESET: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheWrapNodeSet(ctxt, + xmlXPathNodeSetMerge(NULL, val->nodesetval))); + case XPATH_STRING: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheNewString(ctxt, val->stringval)); + case XPATH_BOOLEAN: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheNewBoolean(ctxt, val->boolval)); + case XPATH_NUMBER: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheNewFloat(ctxt, val->floatval)); + default: + break; + } + return(xmlXPathObjectCopy(val)); +} + +/** + * xmlXPathCacheConvertBoolean: + * @ctxt: the XPath context + * @val: an XPath object + * + * This is the cached version of xmlXPathConvertBoolean(). + * Converts an existing object to its boolean() equivalent + * + * Returns a created or reused object, the old one is freed (or the operation + * is done directly on @val) + */ +static xmlXPathObjectPtr +xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(xmlXPathCacheNewBoolean(ctxt, 0)); + if (val->type == XPATH_BOOLEAN) + return(val); + ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val)); + xmlXPathReleaseObject(ctxt, val); + return(ret); +} + +/** + * xmlXPathCacheConvertNumber: + * @ctxt: the XPath context + * @val: an XPath object + * + * This is the cached version of xmlXPathConvertNumber(). + * Converts an existing object to its number() equivalent + * + * Returns a created or reused object, the old one is freed (or the operation + * is done directly on @val) + */ +static xmlXPathObjectPtr +xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(xmlXPathCacheNewFloat(ctxt, 0.0)); + if (val->type == XPATH_NUMBER) + return(val); + ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val)); + xmlXPathReleaseObject(ctxt, val); + return(ret); +} + +/************************************************************************ + * * * Parser stacks related functions and macros * * * ************************************************************************/ @@ -1204,7 +2479,7 @@ xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) { ret = xmlXPathCastToBoolean(obj); else ret = obj->boolval; - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); return(ret); } @@ -1231,7 +2506,7 @@ xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) { ret = xmlXPathCastToNumber(obj); else ret = obj->floatval; - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); return(ret); } @@ -1258,7 +2533,7 @@ xmlXPathPopString (xmlXPathParserContextPtr ctxt) { /* TODO: needs refactoring somewhere else */ if (obj->stringval == ret) obj->stringval = NULL; - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); return(ret); } @@ -1292,7 +2567,8 @@ xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) { if (obj->boolval && obj->user != NULL) xmlFreeNodeList((xmlNodePtr) obj->user); #endif - xmlXPathFreeNodeSetList(obj); + obj->nodesetval = NULL; + xmlXPathReleaseObject(ctxt->context, obj); return(ret); } @@ -1320,7 +2596,8 @@ xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) { } obj = valuePop(ctxt); ret = obj->user; - xmlXPathFreeObject(obj); + obj->user = NULL; + xmlXPathReleaseObject(ctxt->context, obj); return(ret); } @@ -1691,6 +2968,297 @@ xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) { return(-1); /* assume there is no sibling list corruption */ } +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON +/** + * xmlXPathCmpNodesExt: + * @node1: the first node + * @node2: the second node + * + * Compare two nodes w.r.t document order. + * This one is optimized for handling of non-element nodes. + * + * Returns -2 in case of error 1 if first point < second point, 0 if + * it's the same node, -1 otherwise + */ +static int +xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) { + int depth1, depth2; + int misc = 0, precedence1 = 0, precedence2 = 0; + xmlNodePtr miscNode1 = NULL, miscNode2 = NULL; + xmlNodePtr cur, root; + long l1, l2; + + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + + if (node1 == node2) + return(0); + + /* + * a couple of optimizations which will avoid computations in most cases + */ + switch (node1->type) { + case XML_ELEMENT_NODE: + if (node2->type == XML_ELEMENT_NODE) { + if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */ + (0 > (long) node2->content) && + (node1->doc == node2->doc)) + { + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } else + goto turtle_comparison; + } + break; + case XML_ATTRIBUTE_NODE: + precedence1 = 1; /* element is owner */ + miscNode1 = node1; + node1 = node1->parent; + misc = 1; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: { + miscNode1 = node1; + /* + * Find nearest element node. + */ + if (node1->prev != NULL) { + do { + node1 = node1->prev; + if (node1->type == XML_ELEMENT_NODE) { + precedence1 = 3; /* element in prev-sibl axis */ + break; + } + if (node1->prev == NULL) { + precedence1 = 2; /* element is parent */ + /* + * URGENT TODO: Are there any cases, where the + * parent of such a node is not an element node? + */ + node1 = node1->parent; + break; + } + } while (1); + } else { + precedence1 = 2; /* element is parent */ + node1 = node1->parent; + } + if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE)) { + /* + * Fallback for whatever case. + */ + node1 = miscNode1; + precedence1 = 0; + } else + misc = 1; + } + break; + case XML_NAMESPACE_DECL: + /* + * TODO: why do we return 1 for namespace nodes? + */ + return(1); + default: + break; + } + switch (node2->type) { + case XML_ELEMENT_NODE: + break; + case XML_ATTRIBUTE_NODE: + precedence2 = 1; /* element is owner */ + miscNode2 = node2; + node2 = node2->parent; + misc = 1; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: { + miscNode2 = node2; + if (node2->prev != NULL) { + do { + node2 = node2->prev; + if (node2->type == XML_ELEMENT_NODE) { + precedence2 = 3; /* element in prev-sibl axis */ + break; + } + if (node2->prev == NULL) { + precedence2 = 2; /* element is parent */ + node2 = node2->parent; + break; + } + } while (1); + } else { + precedence2 = 2; /* element is parent */ + node2 = node2->parent; + } + if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) || + (0 <= (long) node1->content)) + { + node2 = miscNode2; + precedence2 = 0; + } else + misc = 1; + } + break; + case XML_NAMESPACE_DECL: + return(1); + default: + break; + } + if (misc) { + if (node1 == node2) { + if (precedence1 == precedence2) { + /* + * The ugly case; but normally there aren't many + * adjacent non-element nodes around. + */ + cur = miscNode2->prev; + while (cur != NULL) { + if (cur == miscNode1) + return(1); + if (cur->type == XML_ELEMENT_NODE) + return(-1); + cur = cur->prev; + } + return (-1); + } else { + /* + * Evaluate based on higher precedence wrt to the element. + * TODO: This assumes attributes are sorted before content. + * Is this 100% correct? + */ + if (precedence1 < precedence2) + return(1); + else + return(-1); + } + } + /* + * Special case: One of the helper-elements is contained by the other. + * <foo> + * <node2> + * <node1>Text-1(precedence1 == 2)</node1> + * </node2> + * Text-6(precedence2 == 3) + * </foo> + */ + if ((precedence2 == 3) && (precedence1 > 1)) { + cur = node1->parent; + while (cur) { + if (cur == node2) + return(1); + cur = cur->parent; + } + } + if ((precedence1 == 3) && (precedence2 > 1)) { + cur = node2->parent; + while (cur) { + if (cur == node1) + return(-1); + cur = cur->parent; + } + } + } + + /* + * Speedup using document order if availble. + */ + if ((node1->type == XML_ELEMENT_NODE) && + (node2->type == XML_ELEMENT_NODE) && + (0 > (long) node1->content) && + (0 > (long) node2->content) && + (node1->doc == node2->doc)) { + + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } + +turtle_comparison: + + if (node1 == node2->prev) + return(1); + if (node1 == node2->next) + return(-1); + /* + * compute depth to root + */ + for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { + if (cur == node1) + return(1); + depth2++; + } + root = cur; + for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { + if (cur == node2) + return(-1); + depth1++; + } + /* + * Distinct document (or distinct entities :-( ) case. + */ + if (root != cur) { + return(-2); + } + /* + * get the nearest common ancestor. + */ + while (depth1 > depth2) { + depth1--; + node1 = node1->parent; + } + while (depth2 > depth1) { + depth2--; + node2 = node2->parent; + } + while (node1->parent != node2->parent) { + node1 = node1->parent; + node2 = node2->parent; + /* should not happen but just in case ... */ + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + } + /* + * Find who's first. + */ + if (node1 == node2->prev) + return(1); + if (node1 == node2->next) + return(-1); + /* + * Speedup using document order if availble. + */ + if ((node1->type == XML_ELEMENT_NODE) && + (node2->type == XML_ELEMENT_NODE) && + (0 > (long) node1->content) && + (0 > (long) node2->content) && + (node1->doc == node2->doc)) { + + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } + + for (cur = node1->next;cur != NULL;cur = cur->next) + if (cur == node2) + return(1); + return(-1); /* assume there is no sibling list corruption */ +} +#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */ + /** * xmlXPathNodeSetSort: * @set: the node set @@ -1711,8 +3279,14 @@ xmlXPathNodeSetSort(xmlNodeSetPtr set) { for (i = incr; i < len; i++) { j = i - incr; while (j >= 0) { +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + if (xmlXPathCmpNodesExt(set->nodeTab[j], + set->nodeTab[j + incr]) == -1) +#else if (xmlXPathCmpNodes(set->nodeTab[j], - set->nodeTab[j + incr]) == -1) { + set->nodeTab[j + incr]) == -1) +#endif + { tmp = set->nodeTab[j]; set->nodeTab[j] = set->nodeTab[j + incr]; set->nodeTab[j + incr] = tmp; @@ -2044,31 +3618,60 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { xmlNodeSetPtr xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { int i, j, initNr, skip; + xmlNodePtr n1, n2; if (val2 == NULL) return(val1); if (val1 == NULL) { val1 = xmlXPathNodeSetCreate(NULL); +#if 0 + /* + * TODO: The optimization won't work in every case, since + * those nasty namespace nodes need to be added with + * xmlXPathNodeSetDupNs() to the set; thus a pure + * memcpy is not possible. + * If there was a flag on the nodesetval, indicating that + * some temporary nodes are in, that would be helpfull. + */ + /* + * Optimization: Create an equally sized node-set + * and memcpy the content. + */ + val1 = xmlXPathNodeSetCreateSize(val2->nodeNr); + if (val1 == NULL) + return(NULL); + if (val2->nodeNr != 0) { + if (val2->nodeNr == 1) + *(val1->nodeTab) = *(val2->nodeTab); + else { + memcpy(val1->nodeTab, val2->nodeTab, + val2->nodeNr * sizeof(xmlNodePtr)); + } + val1->nodeNr = val2->nodeNr; + } + return(val1); +#endif } /* @@ with_ns to check whether namespace nodes should be looked at @@ */ initNr = val1->nodeNr; for (i = 0;i < val2->nodeNr;i++) { + n2 = val2->nodeTab[i]; /* * check against duplicates */ skip = 0; for (j = 0; j < initNr; j++) { - if (val1->nodeTab[j] == val2->nodeTab[i]) { + n1 = val1->nodeTab[j]; + if (n1 == n2) { skip = 1; break; - } else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) && - (val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) { - xmlNsPtr ns1, ns2; - ns1 = (xmlNsPtr) val1->nodeTab[j]; - ns2 = (xmlNsPtr) val2->nodeTab[i]; - if ((ns1->next == ns2->next) && - (xmlStrEqual(ns1->prefix, ns2->prefix))) { + } else if ((n1->type == XML_NAMESPACE_DECL) && + (n2->type == XML_NAMESPACE_DECL)) { + if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) && + (xmlStrEqual(((xmlNsPtr) n1)->prefix, + ((xmlNsPtr) n2)->prefix))) + { skip = 1; break; } @@ -2102,13 +3705,13 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { } val1->nodeTab = temp; } - if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { - xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; + if (n2->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) n2; val1->nodeTab[val1->nodeNr++] = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); } else - val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; + val1->nodeTab[val1->nodeNr++] = n2; } return(val1); @@ -2253,6 +3856,34 @@ xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { } /** + * xmlXPathNodeSetClear: + * @set: the xmlNodeSetPtr to free + * + * Clears the list from all temporary XPath objects (e.g. namespace nodes + * are feed), but does *not* free the list itself. Sets the length of the + * list to 0. + */ +static void +xmlXPathNodeSetClear(xmlNodeSetPtr set) +{ + int i; + xmlNodePtr node; + + if ((set == NULL) || (set->nodeNr <= 0)) + return; + + for (i = 0; i < set->nodeNr; i++) { + node = set->nodeTab[i]; + if ((node != NULL) && + (node->type == XML_NAMESPACE_DECL)) + { + xmlXPathNodeSetFreeNs((xmlNsPtr) node); + } + } + set->nodeNr = 0; +} + +/** * xmlXPathFreeValueTree: * @obj: the xmlNodeSetPtr to free * @@ -2344,6 +3975,9 @@ xmlXPathNewNodeSet(xmlNodePtr val) { ret->boolval = 0; ret->nodesetval = xmlXPathNodeSetCreate(val); /* @@ with_ns to check whether namespace nodes should be looked at @@ */ +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET); +#endif return(ret); } @@ -2370,6 +4004,9 @@ xmlXPathNewValueTree(xmlNodePtr val) { ret->boolval = 1; ret->user = (void *) val; ret->nodesetval = xmlXPathNodeSetCreate(val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE); +#endif return(ret); } @@ -2421,6 +4058,9 @@ xmlXPathWrapNodeSet(xmlNodeSetPtr val) { memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_NODESET; ret->nodesetval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET); +#endif return(ret); } @@ -2434,6 +4074,9 @@ xmlXPathWrapNodeSet(xmlNodeSetPtr val) { void xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) { if (obj == NULL) return; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(NULL, obj->type); +#endif xmlFree(obj); } @@ -3070,7 +4713,7 @@ xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, if (name == NULL) return(NULL); - return(xmlXPathObjectCopy((xmlXPathObjectPtr) + return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri))); } @@ -3197,6 +4840,9 @@ xmlXPathNewFloat(double val) { memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_NUMBER; ret->floatval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER); +#endif return(ret); } @@ -3220,6 +4866,9 @@ xmlXPathNewBoolean(int val) { memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_BOOLEAN; ret->boolval = (val != 0); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN); +#endif return(ret); } @@ -3246,6 +4895,9 @@ xmlXPathNewString(const xmlChar *val) { ret->stringval = xmlStrdup(val); else ret->stringval = xmlStrdup((const xmlChar *)""); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING); +#endif return(ret); } @@ -3269,6 +4921,9 @@ xmlXPathWrapString (xmlChar *val) { memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_STRING; ret->stringval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING); +#endif return(ret); } @@ -3292,6 +4947,9 @@ xmlXPathNewCString(const char *val) { memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_STRING; ret->stringval = xmlStrdup(BAD_CAST val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING); +#endif return(ret); } @@ -3328,6 +4986,9 @@ xmlXPathWrapExternal (void *val) { memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); ret->type = XPATH_USERS; ret->user = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS); +#endif return(ret); } @@ -3352,6 +5013,9 @@ xmlXPathObjectCopy(xmlXPathObjectPtr val) { return(NULL); } memcpy(ret, val , (size_t) sizeof(xmlXPathObject)); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, val->type); +#endif switch (val->type) { case XPATH_BOOLEAN: case XPATH_NUMBER: @@ -3436,8 +5100,9 @@ xmlXPathFreeObject(xmlXPathObjectPtr obj) { xmlFreeNodeList((xmlNodePtr) obj->user); } else #endif + obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */ if (obj->nodesetval != NULL) - xmlXPathFreeValueTree(obj->nodesetval); + xmlXPathFreeValueTree(obj->nodesetval); } else { if (obj->nodesetval != NULL) xmlXPathFreeNodeSet(obj->nodesetval); @@ -3451,8 +5116,153 @@ xmlXPathFreeObject(xmlXPathObjectPtr obj) { if (obj->stringval != NULL) xmlFree(obj->stringval); } +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(NULL, obj->type); +#endif + xmlFree(obj); +} - xmlFree(obj); +/** + * xmlXPathReleaseObject: + * @obj: the xmlXPathObjectPtr to free or to cache + * + * Depending on the state of the cache this frees the given + * XPath object or stores it in the cache. + */ +static void +xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj) +{ +#define XP_CACHE_ADD(sl, o) if (sl == NULL) { \ + sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \ + if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj; + +#define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n)) + + if (obj == NULL) + return; + if ((ctxt == NULL) || (ctxt->cache == NULL)) { + xmlXPathFreeObject(obj); + } else { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + switch (obj->type) { + case XPATH_NODESET: + case XPATH_XSLT_TREE: + if (obj->nodesetval != NULL) { + if (obj->boolval) { + /* + * It looks like the @boolval is used for + * evaluation if this an XSLT Result Tree Fragment. + * TODO: Check if this assumption is correct. + */ + obj->type = XPATH_XSLT_TREE; /* just for debugging */ + xmlXPathFreeValueTree(obj->nodesetval); + obj->nodesetval = NULL; + } else if ((obj->nodesetval->nodeMax <= 40) && + (XP_CACHE_WANTS(cache->nodesetObjs, + cache->maxNodeset))) + { + XP_CACHE_ADD(cache->nodesetObjs, obj); + goto obj_cached; + } else { + xmlXPathFreeNodeSet(obj->nodesetval); + obj->nodesetval = NULL; + } + } + break; + case XPATH_STRING: + if (obj->stringval != NULL) + xmlFree(obj->stringval); + + if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) { + XP_CACHE_ADD(cache->stringObjs, obj); + goto obj_cached; + } + break; + case XPATH_BOOLEAN: + if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) { + XP_CACHE_ADD(cache->booleanObjs, obj); + goto obj_cached; + } + break; + case XPATH_NUMBER: + if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) { + XP_CACHE_ADD(cache->numberObjs, obj); + goto obj_cached; + } + break; +#ifdef LIBXML_XPTR_ENABLED + case XPATH_LOCATIONSET: + if (obj->user != NULL) { + xmlXPtrFreeLocationSet(obj->user); + } + goto free_obj; +#endif + default: + goto free_obj; + } + + /* + * Fallback to adding to the misc-objects slot. + */ + if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) { + XP_CACHE_ADD(cache->miscObjs, obj); + } else + goto free_obj; + +obj_cached: + +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(ctxt, obj->type); +#endif + + if (obj->nodesetval != NULL) { + xmlNodeSetPtr tmpset = obj->nodesetval; + + /* + * TODO: Due to those nasty ns-nodes, we need to traverse + * the list and free the ns-nodes. + * URGENT TODO: Check if it's actually slowing things down. + * Maybe we shouldn't try to preserve the list. + */ + if (tmpset->nodeNr > 1) { + int i; + xmlNodePtr node; + + for (i = 0; i < tmpset->nodeNr; i++) { + node = tmpset->nodeTab[i]; + if ((node != NULL) && + (node->type == XML_NAMESPACE_DECL)) + { + xmlXPathNodeSetFreeNs((xmlNsPtr) node); + } + } + } else if (tmpset->nodeNr == 1) { + if ((tmpset->nodeTab[0] != NULL) && + (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]); + } + tmpset->nodeNr = 0; + memset(obj, 0, sizeof(xmlXPathObject)); + obj->nodesetval = tmpset; + } else + memset(obj, 0, sizeof(xmlXPathObject)); + + return; + +free_obj: + /* + * Cache is full; free the object. + */ + if (obj->nodesetval != NULL) + xmlXPathFreeNodeSet(obj->nodesetval); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(NULL, obj->type); +#endif + xmlFree(obj); + } + return; } @@ -3540,7 +5350,8 @@ xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) { if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL)) return(xmlStrdup((const xmlChar *) "")); - xmlXPathNodeSetSort(ns); + if (ns->nodeNr > 1) + xmlXPathNodeSetSort(ns); return(xmlXPathCastNodeToString(ns->nodeTab[0])); } @@ -3936,8 +5747,15 @@ xmlXPathNewContext(xmlDocPtr doc) { ret->contextSize = -1; ret->proximityPosition = -1; - xmlXPathRegisterAllFunctions(ret); - +#ifdef XP_DEFAULT_CACHE_ON + if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) { + xmlXPathFreeContext(ret); + return(NULL); + } +#endif + + xmlXPathRegisterAllFunctions(ret); + return(ret); } @@ -3951,6 +5769,8 @@ void xmlXPathFreeContext(xmlXPathContextPtr ctxt) { if (ctxt == NULL) return; + if (ctxt->cache != NULL) + xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache); xmlXPathRegisteredNsCleanup(ctxt); xmlXPathRegisteredFuncsCleanup(ctxt); xmlXPathRegisteredVariablesCleanup(ctxt); @@ -4256,8 +6076,8 @@ xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict, if ((f == NULL) || (arg == NULL) || ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { - xmlXPathFreeObject(arg); - xmlXPathFreeObject(f); + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, f); return(0); } ns = arg->nodesetval; @@ -4266,18 +6086,18 @@ xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict, str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); if (str2 != NULL) { valuePush(ctxt, - xmlXPathNewString(str2)); + xmlXPathCacheNewString(ctxt->context, str2)); xmlFree(str2); xmlXPathNumberFunction(ctxt, 1); - valuePush(ctxt, xmlXPathObjectCopy(f)); + valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f)); ret = xmlXPathCompareValues(ctxt, inf, strict); if (ret) break; } } } - xmlXPathFreeObject(arg); - xmlXPathFreeObject(f); + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, f); return(ret); } @@ -4311,8 +6131,8 @@ xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict, if ((s == NULL) || (arg == NULL) || ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { - xmlXPathFreeObject(arg); - xmlXPathFreeObject(s); + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, s); return(0); } ns = arg->nodesetval; @@ -4321,17 +6141,17 @@ xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict, str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); if (str2 != NULL) { valuePush(ctxt, - xmlXPathNewString(str2)); + xmlXPathCacheNewString(ctxt->context, str2)); xmlFree(str2); - valuePush(ctxt, xmlXPathObjectCopy(s)); + valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s)); ret = xmlXPathCompareValues(ctxt, inf, strict); if (ret) break; } } } - xmlXPathFreeObject(arg); - xmlXPathFreeObject(s); + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, s); return(ret); } @@ -4575,12 +6395,12 @@ xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt, for (i=0;i<ns->nodeNr;i++) { str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); if (str2 != NULL) { - valuePush(ctxt, xmlXPathNewString(str2)); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2)); xmlFree(str2); xmlXPathNumberFunction(ctxt, 1); val = valuePop(ctxt); v = val->floatval; - xmlXPathFreeObject(val); + xmlXPathReleaseObject(ctxt->context, val); if (!xmlXPathIsNaN(v)) { if ((!neq) && (v==f)) { ret = 1; @@ -4899,8 +6719,8 @@ xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt, case XPATH_XSLT_TREE: break; } - xmlXPathFreeObject(arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); return(ret); } @@ -4922,9 +6742,9 @@ xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { arg1 = valuePop(ctxt); if ((arg1 == NULL) || (arg2 == NULL)) { if (arg1 != NULL) - xmlXPathFreeObject(arg1); + xmlXPathReleaseObject(ctxt->context, arg1); else - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); XP_ERROR0(XPATH_INVALID_OPERAND); } @@ -4981,8 +6801,8 @@ xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { TODO break; } - xmlXPathFreeObject(arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); return(ret); } @@ -5007,9 +6827,9 @@ xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) { arg1 = valuePop(ctxt); if ((arg1 == NULL) || (arg2 == NULL)) { if (arg1 != NULL) - xmlXPathFreeObject(arg1); + xmlXPathReleaseObject(ctxt->context, arg1); else - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); XP_ERROR0(XPATH_INVALID_OPERAND); } @@ -5018,7 +6838,7 @@ xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) { xmlGenericError(xmlGenericErrorContext, "NotEqual: by pointer\n"); #endif - xmlXPathFreeObject(arg1); + xmlXPathReleaseObject(ctxt->context, arg1); return(0); } @@ -5066,8 +6886,8 @@ xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) { TODO break; } - xmlXPathFreeObject(arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); return(ret); } @@ -5108,9 +6928,9 @@ xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { arg1 = valuePop(ctxt); if ((arg1 == NULL) || (arg2 == NULL)) { if (arg1 != NULL) - xmlXPathFreeObject(arg1); + xmlXPathReleaseObject(ctxt->context, arg1); else - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); XP_ERROR0(XPATH_INVALID_OPERAND); } @@ -5152,8 +6972,8 @@ xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { arg2 = valuePop(ctxt); } if (arg2->type != XPATH_NUMBER) { - xmlXPathFreeObject(arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); XP_ERROR0(XPATH_INVALID_OPERAND); } /* @@ -5205,8 +7025,8 @@ xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { } } } - xmlXPathFreeObject(arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); return(ret); } @@ -5256,8 +7076,7 @@ xmlXPathAddValues(xmlXPathParserContextPtr ctxt) { if (arg == NULL) XP_ERROR(XPATH_INVALID_OPERAND); val = xmlXPathCastToNumber(arg); - xmlXPathFreeObject(arg); - + xmlXPathReleaseObject(ctxt->context, arg); CAST_TO_NUMBER; CHECK_TYPE(XPATH_NUMBER); ctxt->value->floatval += val; @@ -5280,8 +7099,7 @@ xmlXPathSubValues(xmlXPathParserContextPtr ctxt) { if (arg == NULL) XP_ERROR(XPATH_INVALID_OPERAND); val = xmlXPathCastToNumber(arg); - xmlXPathFreeObject(arg); - + xmlXPathReleaseObject(ctxt->context, arg); CAST_TO_NUMBER; CHECK_TYPE(XPATH_NUMBER); ctxt->value->floatval -= val; @@ -5304,8 +7122,7 @@ xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { if (arg == NULL) XP_ERROR(XPATH_INVALID_OPERAND); val = xmlXPathCastToNumber(arg); - xmlXPathFreeObject(arg); - + xmlXPathReleaseObject(ctxt->context, arg); CAST_TO_NUMBER; CHECK_TYPE(XPATH_NUMBER); ctxt->value->floatval *= val; @@ -5328,8 +7145,7 @@ xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { if (arg == NULL) XP_ERROR(XPATH_INVALID_OPERAND); val = xmlXPathCastToNumber(arg); - xmlXPathFreeObject(arg); - + xmlXPathReleaseObject(ctxt->context, arg); CAST_TO_NUMBER; CHECK_TYPE(XPATH_NUMBER); if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval)) @@ -5370,8 +7186,7 @@ xmlXPathModValues(xmlXPathParserContextPtr ctxt) { if (arg == NULL) XP_ERROR(XPATH_INVALID_OPERAND); arg2 = xmlXPathCastToNumber(arg); - xmlXPathFreeObject(arg); - + xmlXPathReleaseObject(ctxt->context, arg); CAST_TO_NUMBER; CHECK_TYPE(XPATH_NUMBER); arg1 = ctxt->value->floatval; @@ -5396,6 +7211,17 @@ xmlXPathModValues(xmlXPathParserContextPtr ctxt) { typedef xmlNodePtr (*xmlXPathTraversalFunction) (xmlXPathParserContextPtr ctxt, xmlNodePtr cur); +/* + * xmlXPathTraversalFunctionExt: + * A traversal function enumerates nodes along an axis. + * Initially it must be called with NULL, and it indicates + * termination on the axis by returning NULL. + * The context node of the traversal is specified via @contextNode. + */ +typedef xmlNodePtr (*xmlXPathTraversalFunctionExt) + (xmlNodePtr cur, xmlNodePtr contextNode); + + /** * xmlXPathNextSelf: * @ctxt: the XPath Parser context @@ -5466,6 +7292,157 @@ xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { } /** + * xmlXPathNextChildElement: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "child" direction and nodes of type element. + * The child axis contains the children of the context node in document order. + * + * Returns the next element following that axis + */ +static xmlNodePtr +xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) { + cur = ctxt->context->node; + if (cur == NULL) return(NULL); + /* + * Get the first element child. + */ + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */ + case XML_ENTITY_NODE: + cur = cur->children; + if (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) + return(cur); + do { + cur = cur->next; + } while ((cur != NULL) && + (cur->type != XML_ELEMENT_NODE)); + return(cur); + } + return(NULL); + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + return(xmlDocGetRootElement((xmlDocPtr) cur)); + default: + return(NULL); + } + return(NULL); + } + /* + * Get the next sibling element node. + */ + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_XINCLUDE_END: + break; + /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */ + default: + return(NULL); + } + if (cur->next != NULL) { + if (cur->next->type == XML_ELEMENT_NODE) + return(cur->next); + cur = cur->next; + do { + cur = cur->next; + } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE)); + return(cur); + } + return(NULL); +} + +/** + * xmlXPathNextDescendantOrSelfElemParent: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "descendant-or-self" axis. + * Additionally it returns only nodes which can be parents of + * element nodes. + * + * + * Returns the next element following that axis + */ +static xmlNodePtr +xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur, + xmlNodePtr contextNode) +{ + if (cur == NULL) { + if (contextNode == NULL) + return(NULL); + switch (contextNode->type) { + case XML_ELEMENT_NODE: + case XML_XINCLUDE_START: + case XML_DOCUMENT_FRAG_NODE: + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + return(contextNode); + default: + return(NULL); + } + return(NULL); + } else { + xmlNodePtr start = cur; + + while (cur != NULL) { + switch (cur->type) { + case XML_ELEMENT_NODE: + /* TODO: OK to have XInclude here? */ + case XML_XINCLUDE_START: + case XML_DOCUMENT_FRAG_NODE: + if (cur != start) + return(cur); + if (cur->children != NULL) { + cur = cur->children; + continue; + } + break; +#ifdef LIBXML_DOCB_ENABLED + /* Not sure if we need those here. */ + case XML_DOCUMENT_NODE: + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + if (cur != start) + return(cur); + return(xmlDocGetRootElement((xmlDocPtr) cur)); + default: + break; + } + +next_sibling: + if ((cur == NULL) || (cur == contextNode)) + return(NULL); + if (cur->next != NULL) { + cur = cur->next; + } else { + cur = cur->parent; + goto next_sibling; + } + } + } + return(NULL); +} + +/** * xmlXPathNextDescendant: * @ctxt: the XPath Parser context * @cur: the current node in the traversal @@ -6051,9 +8028,11 @@ xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { */ void xmlXPathRoot(xmlXPathParserContextPtr ctxt) { - if ((ctxt == NULL) || (ctxt->context == NULL)) return; + if ((ctxt == NULL) || (ctxt->context == NULL)) + return; ctxt->context->node = (xmlNodePtr) ctxt->context->doc; - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); } /************************************************************************ @@ -6077,7 +8056,9 @@ void xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); if (ctxt->context->contextSize >= 0) { - valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize)); + valuePush(ctxt, + xmlXPathCacheNewFloat(ctxt->context, + (double) ctxt->context->contextSize)); #ifdef DEBUG_EXPR xmlGenericError(xmlGenericErrorContext, "last() : %d\n", ctxt->context->contextSize); @@ -6103,7 +8084,8 @@ xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); if (ctxt->context->proximityPosition >= 0) { valuePush(ctxt, - xmlXPathNewFloat((double) ctxt->context->proximityPosition)); + xmlXPathCacheNewFloat(ctxt->context, + (double) ctxt->context->proximityPosition)); #ifdef DEBUG_EXPR xmlGenericError(xmlGenericErrorContext, "position() : %d\n", ctxt->context->proximityPosition); @@ -6133,13 +8115,14 @@ xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { cur = valuePop(ctxt); if ((cur == NULL) || (cur->nodesetval == NULL)) - valuePush(ctxt, xmlXPathNewFloat((double) 0)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0)); else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) { - valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, + (double) cur->nodesetval->nodeNr)); } else { if ((cur->nodesetval->nodeNr != 1) || (cur->nodesetval->nodeTab == NULL)) { - valuePush(ctxt, xmlXPathNewFloat((double) 0)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0)); } else { xmlNodePtr tmp; int i = 0; @@ -6152,10 +8135,10 @@ xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { i++; } } - valuePush(ctxt, xmlXPathNewFloat((double) i)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) i)); } } - xmlXPathFreeObject(cur); + xmlXPathReleaseObject(ctxt->context, cur); } /** @@ -6257,17 +8240,14 @@ xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlFree(tokens); } } - - xmlXPathFreeObject(obj); - valuePush(ctxt, xmlXPathWrapNodeSet(ret)); + xmlXPathReleaseObject(ctxt->context, obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret)); return; } - obj = xmlXPathConvertString(obj); - + obj = xmlXPathCacheConvertString(ctxt->context, obj); ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval); - valuePush(ctxt, xmlXPathWrapNodeSet(ret)); - - xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret)); + xmlXPathReleaseObject(ctxt->context, obj); return; } @@ -6291,7 +8271,8 @@ xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt == NULL) return; if (nargs == 0) { - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); nargs = 1; } @@ -6303,7 +8284,7 @@ xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { cur = valuePop(ctxt); if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); } else { int i = 0; /* Should be first in document order !!!!! */ switch (cur->nodesetval->nodeTab[i]->type) { @@ -6311,20 +8292,21 @@ xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { case XML_ATTRIBUTE_NODE: case XML_PI_NODE: if (cur->nodesetval->nodeTab[i]->name[0] == ' ') - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); else valuePush(ctxt, - xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); + xmlXPathCacheNewString(ctxt->context, + cur->nodesetval->nodeTab[i]->name)); break; case XML_NAMESPACE_DECL: - valuePush(ctxt, xmlXPathNewString( + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix)); break; default: - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); } } - xmlXPathFreeObject(cur); + xmlXPathReleaseObject(ctxt->context, cur); } /** @@ -6348,7 +8330,8 @@ xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt == NULL) return; if (nargs == 0) { - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); nargs = 1; } CHECK_ARITY(1); @@ -6359,23 +8342,23 @@ xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) { cur = valuePop(ctxt); if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); } else { int i = 0; /* Should be first in document order !!!!! */ switch (cur->nodesetval->nodeTab[i]->type) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: if (cur->nodesetval->nodeTab[i]->ns == NULL) - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); else - valuePush(ctxt, xmlXPathNewString( + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, cur->nodesetval->nodeTab[i]->ns->href)); break; default: - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); } } - xmlXPathFreeObject(cur); + xmlXPathReleaseObject(ctxt->context, cur); } /** @@ -6406,7 +8389,8 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) xmlXPathObjectPtr cur; if (nargs == 0) { - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); nargs = 1; } @@ -6418,7 +8402,7 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) cur = valuePop(ctxt); if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); } else { int i = 0; /* Should be first in document order !!!!! */ @@ -6426,12 +8410,13 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: if (cur->nodesetval->nodeTab[i]->name[0] == ' ') - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, + xmlXPathCacheNewCString(ctxt->context, "")); else if ((cur->nodesetval->nodeTab[i]->ns == NULL) || (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) { - valuePush(ctxt, - xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); - + valuePush(ctxt, + xmlXPathCacheNewString(ctxt->context, + cur->nodesetval->nodeTab[i]->name)); } else { xmlChar *fullname; @@ -6443,16 +8428,17 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) if (fullname == NULL) { XP_ERROR(XPATH_MEMORY_ERROR); } - valuePush(ctxt, xmlXPathWrapString(fullname)); + valuePush(ctxt, xmlXPathCacheWrapString( + ctxt->context, fullname)); } break; default: - valuePush(ctxt, - xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i])); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + cur->nodesetval->nodeTab[i])); xmlXPathLocalNameFunction(ctxt, 1); } } - xmlXPathFreeObject(cur); + xmlXPathReleaseObject(ctxt->context, cur); } @@ -6498,17 +8484,16 @@ xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt == NULL) return; if (nargs == 0) { - valuePush(ctxt, - xmlXPathWrapString( - xmlXPathCastNodeToString(ctxt->context->node))); + valuePush(ctxt, + xmlXPathCacheWrapString(ctxt->context, + xmlXPathCastNodeToString(ctxt->context->node))); return; } CHECK_ARITY(1); cur = valuePop(ctxt); if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); - cur = xmlXPathConvertString(cur); - valuePush(ctxt, cur); + valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur)); } /** @@ -6531,12 +8516,13 @@ xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { if ((ctxt == NULL) || (ctxt->context == NULL)) return; if (ctxt->context->node == NULL) { - valuePush(ctxt, xmlXPathNewFloat(0)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0)); } else { xmlChar *content; content = xmlXPathCastNodeToString(ctxt->context->node); - valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content))); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, + xmlUTF8Strlen(content))); xmlFree(content); } return; @@ -6545,8 +8531,9 @@ xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { CAST_TO_STRING; CHECK_TYPE(XPATH_STRING); cur = valuePop(ctxt); - valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval))); - xmlXPathFreeObject(cur); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, + xmlUTF8Strlen(cur->stringval))); + xmlXPathReleaseObject(ctxt->context, cur); } /** @@ -6571,7 +8558,7 @@ xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { CAST_TO_STRING; cur = valuePop(ctxt); if ((cur == NULL) || (cur->type != XPATH_STRING)) { - xmlXPathFreeObject(cur); + xmlXPathReleaseObject(ctxt->context, cur); return; } nargs--; @@ -6580,15 +8567,14 @@ xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { CAST_TO_STRING; newobj = valuePop(ctxt); if ((newobj == NULL) || (newobj->type != XPATH_STRING)) { - xmlXPathFreeObject(newobj); - xmlXPathFreeObject(cur); + xmlXPathReleaseObject(ctxt->context, newobj); + xmlXPathReleaseObject(ctxt->context, cur); XP_ERROR(XPATH_INVALID_TYPE); } tmp = xmlStrcat(newobj->stringval, cur->stringval); newobj->stringval = cur->stringval; cur->stringval = tmp; - - xmlXPathFreeObject(newobj); + xmlXPathReleaseObject(ctxt->context, newobj); nargs--; } valuePush(ctxt, cur); @@ -6614,17 +8600,18 @@ xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) { needle = valuePop(ctxt); CAST_TO_STRING; hay = valuePop(ctxt); + if ((hay == NULL) || (hay->type != XPATH_STRING)) { - xmlXPathFreeObject(hay); - xmlXPathFreeObject(needle); + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); XP_ERROR(XPATH_INVALID_TYPE); } if (xmlStrstr(hay->stringval, needle->stringval)) - valuePush(ctxt, xmlXPathNewBoolean(1)); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1)); else - valuePush(ctxt, xmlXPathNewBoolean(0)); - xmlXPathFreeObject(hay); - xmlXPathFreeObject(needle); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0)); + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); } /** @@ -6648,18 +8635,19 @@ xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) { needle = valuePop(ctxt); CAST_TO_STRING; hay = valuePop(ctxt); + if ((hay == NULL) || (hay->type != XPATH_STRING)) { - xmlXPathFreeObject(hay); - xmlXPathFreeObject(needle); + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); XP_ERROR(XPATH_INVALID_TYPE); } n = xmlStrlen(needle->stringval); if (xmlStrncmp(hay->stringval, needle->stringval, n)) - valuePush(ctxt, xmlXPathNewBoolean(0)); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0)); else - valuePush(ctxt, xmlXPathNewBoolean(1)); - xmlXPathFreeObject(hay); - xmlXPathFreeObject(needle); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1)); + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); } /** @@ -6711,14 +8699,14 @@ xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_TYPE(XPATH_NUMBER); len = valuePop(ctxt); le = len->floatval; - xmlXPathFreeObject(len); + xmlXPathReleaseObject(ctxt->context, len); } CAST_TO_NUMBER; CHECK_TYPE(XPATH_NUMBER); start = valuePop(ctxt); in = start->floatval; - xmlXPathFreeObject(start); + xmlXPathReleaseObject(ctxt->context, start); CAST_TO_STRING; CHECK_TYPE(XPATH_STRING); str = valuePop(ctxt); @@ -6777,15 +8765,13 @@ xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { else { ret = NULL; } - if (ret == NULL) - valuePush(ctxt, xmlXPathNewCString("")); + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); else { - valuePush(ctxt, xmlXPathNewString(ret)); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret)); xmlFree(ret); } - - xmlXPathFreeObject(str); + xmlXPathReleaseObject(ctxt->context, str); } /** @@ -6822,12 +8808,12 @@ xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { offset = (int)(point - str->stringval); xmlBufferAdd(target, str->stringval, offset); } - valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); xmlBufferFree(target); } - - xmlXPathFreeObject(str); - xmlXPathFreeObject(find); + xmlXPathReleaseObject(ctxt->context, str); + xmlXPathReleaseObject(ctxt->context, find); } /** @@ -6866,12 +8852,12 @@ xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlBufferAdd(target, &str->stringval[offset], xmlStrlen(str->stringval) - offset); } - valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); xmlBufferFree(target); } - - xmlXPathFreeObject(str); - xmlXPathFreeObject(find); + xmlXPathReleaseObject(ctxt->context, str); + xmlXPathReleaseObject(ctxt->context, find); } /** @@ -6898,9 +8884,9 @@ xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt == NULL) return; if (nargs == 0) { /* Use current context node */ - valuePush(ctxt, - xmlXPathWrapString( - xmlXPathCastNodeToString(ctxt->context->node))); + valuePush(ctxt, + xmlXPathCacheWrapString(ctxt->context, + xmlXPathCastNodeToString(ctxt->context->node))); nargs = 1; } @@ -6931,11 +8917,11 @@ xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { } source++; } - - valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); xmlBufferFree(target); } - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); } /** @@ -7014,11 +9000,12 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { } } } - valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); xmlBufferFree(target); - xmlXPathFreeObject(str); - xmlXPathFreeObject(from); - xmlXPathFreeObject(to); + xmlXPathReleaseObject(ctxt->context, str); + xmlXPathReleaseObject(ctxt->context, from); + xmlXPathReleaseObject(ctxt->context, to); } /** @@ -7041,7 +9028,7 @@ xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); cur = valuePop(ctxt); if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); - cur = xmlXPathConvertBoolean(cur); + cur = xmlXPathCacheConvertBoolean(ctxt->context, cur); valuePush(ctxt, cur); } @@ -7074,7 +9061,7 @@ xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) { void xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); - valuePush(ctxt, xmlXPathNewBoolean(1)); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1)); } /** @@ -7088,7 +9075,7 @@ xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { void xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); - valuePush(ctxt, xmlXPathNewBoolean(0)); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0)); } /** @@ -7136,8 +9123,9 @@ xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) { not_equal: if (theLang != NULL) xmlFree((void *)theLang); - xmlXPathFreeObject(val); - valuePush(ctxt, xmlXPathNewBoolean(ret)); + + xmlXPathReleaseObject(ctxt->context, val); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret)); } /** @@ -7156,12 +9144,12 @@ xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { if (ctxt == NULL) return; if (nargs == 0) { if (ctxt->context->node == NULL) { - valuePush(ctxt, xmlXPathNewFloat(0.0)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0)); } else { xmlChar* content = xmlNodeGetContent(ctxt->context->node); res = xmlXPathStringEvalNumber(content); - valuePush(ctxt, xmlXPathNewFloat(res)); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res)); xmlFree(content); } return; @@ -7169,8 +9157,7 @@ xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(1); cur = valuePop(ctxt); - cur = xmlXPathConvertNumber(cur); - valuePush(ctxt, cur); + valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur)); } /** @@ -7201,8 +9188,8 @@ xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]); } } - valuePush(ctxt, xmlXPathNewFloat(res)); - xmlXPathFreeObject(cur); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res)); + xmlXPathReleaseObject(ctxt->context, cur); } /* @@ -7331,7 +9318,7 @@ xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { * a few forward declarations since we use a recursive call based * implementation. */ -static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt); +static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort); static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter); static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt); static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt); @@ -7810,7 +9797,7 @@ xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) ret *= pow(10.0, (double) exponent); } PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, - xmlXPathNewFloat(ret), NULL); + xmlXPathCacheNewFloat(ctxt->context, ret), NULL); } /** @@ -7900,7 +9887,7 @@ xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) { } if (ret == NULL) return; PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, - xmlXPathNewString(ret), NULL); + xmlXPathCacheNewString(ctxt->context, ret), NULL); xmlFree(ret); } @@ -7988,6 +9975,7 @@ xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) { xmlChar *name; xmlChar *prefix; int nbargs = 0; + int sort = 1; name = xmlXPathParseQName(ctxt, &prefix); if (name == NULL) { @@ -8009,12 +9997,20 @@ xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) { NEXT; SKIP_BLANKS; + /* + * Optimization for count(): we don't need the node-set to be sorted. + */ + if ((prefix == NULL) && (name[0] == 'c') && + xmlStrEqual(name, BAD_CAST "count")) + { + sort = 0; + } ctxt->comp->last = -1; if (CUR != ')') { while (CUR != 0) { int op1 = ctxt->comp->last; ctxt->comp->last = -1; - xmlXPathCompileExpr(ctxt); + xmlXPathCompileExpr(ctxt, sort); CHECK_ERROR; PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0); nbargs++; @@ -8051,7 +10047,7 @@ xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) { else if (CUR == '(') { NEXT; SKIP_BLANKS; - xmlXPathCompileExpr(ctxt); + xmlXPathCompileExpr(ctxt, 1); CHECK_ERROR; if (CUR != ')') { XP_ERROR(XPATH_EXPR_ERROR); @@ -8559,7 +10555,7 @@ xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) { * Parse and compile an expression */ static void -xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) { +xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) { xmlXPathCompAndExpr(ctxt); CHECK_ERROR; SKIP_BLANKS; @@ -8573,8 +10569,13 @@ xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) { op1 = ctxt->comp->nbStep; SKIP_BLANKS; } - if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) { + if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) { /* more ops could be optimized too */ + /* + * This is the main place to eliminate sorting for + * operations which don't require a sorted node-set. + * E.g. count(). + */ PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0); } } @@ -8601,7 +10602,7 @@ xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) { SKIP_BLANKS; ctxt->comp->last = -1; - xmlXPathCompileExpr(ctxt); + xmlXPathCompileExpr(ctxt, 1); CHECK_ERROR; if (CUR != ']') { @@ -8893,7 +10894,7 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { NEXT; SKIP_BLANKS; - xmlXPathCompileExpr(ctxt); + xmlXPathCompileExpr(ctxt, 1); /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */ CHECK_ERROR; @@ -9119,32 +11120,41 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, const xmlChar *URI = NULL; #ifdef DEBUG_STEP - int n = 0; + int nbMatches = 0; #endif - int i, t = 0; - xmlNodeSetPtr ret, list; + int inputIdx, total = 0, specialNodeInSet = 0; + xmlNodeSetPtr inputList, resultList, list; xmlXPathTraversalFunction next = NULL; + xmlXPathTraversalFunctionExt compoundNext = NULL; void (*addNode) (xmlNodeSetPtr, xmlNodePtr); xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr); - xmlNodePtr cur = NULL; + xmlNodePtr oldContextNode, contextNode, cur, compoundContextNode; xmlXPathObjectPtr obj; - xmlNodeSetPtr nodelist; - xmlNodePtr tmp; + xmlXPathContextPtr xpctxt = ctxt->context; CHECK_TYPE0(XPATH_NODESET); obj = valuePop(ctxt); - addNode = xmlXPathNodeSetAdd; - mergeNodeSet = xmlXPathNodeSetMerge; + + /* + * Setup wrt namespaces. + */ if (prefix != NULL) { - URI = xmlXPathNsLookup(ctxt->context, prefix); + URI = xmlXPathNsLookup(xpctxt, prefix); if (URI == NULL) { xmlXPathFreeObject(obj); XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); } } + #ifdef DEBUG_STEP xmlGenericError(xmlGenericErrorContext, "new step : "); #endif + + /* + * Setup wrt the axis. + */ + addNode = xmlXPathNodeSetAdd; + mergeNodeSet = xmlXPathNodeSetMerge; switch (axis) { case AXIS_ANCESTOR: #ifdef DEBUG_STEP @@ -9175,7 +11185,20 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlGenericError(xmlGenericErrorContext, "axis 'child' "); #endif last = NULL; - next = xmlXPathNextChild; + if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) { + /* + * This iterator will give us only nodes which can + * hold element nodes. + */ + compoundNext = xmlXPathNextDescendantOrSelfElemParent; + } + if ((test == NODE_TEST_NAME) && (type == NODE_TYPE_NODE)) { + /* + * Optimization if an element node type is 'element'. + */ + next = xmlXPathNextChildElement; + } else + next = xmlXPathNextChild; mergeNodeSet = xmlXPathNodeSetMergeUnique; break; case AXIS_DESCENDANT: @@ -9250,18 +11273,17 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, break; } if (next == NULL) { - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(xpctxt, obj); return(0); } - nodelist = obj->nodesetval; - if (nodelist == NULL) { - xmlXPathFreeObject(obj); - valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); + inputList = obj->nodesetval; + if ((inputList == NULL) || (inputList->nodeNr <= 0)) { + xmlXPathReleaseObject(xpctxt, obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL)); return(0); - } - addNode = xmlXPathNodeSetAddUnique; - ret = NULL; + } + #ifdef DEBUG_STEP xmlGenericError(xmlGenericErrorContext, " context contains %d nodes\n", nodelist->nodeNr); @@ -9307,37 +11329,87 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, * principal node type. For example, child::* will * select all element children of the context node */ - tmp = ctxt->context->node; - for (i = 0; i < nodelist->nodeNr; i++) { - ctxt->context->node = nodelist->nodeTab[i]; - - cur = NULL; - list = xmlXPathNodeSetCreate(NULL); + oldContextNode = xpctxt->node; + addNode = xmlXPathNodeSetAddUnique; + resultList = NULL; + list = NULL; + compoundContextNode = NULL; + contextNode = NULL; + inputIdx = 0; + + while ((inputIdx < inputList->nodeNr) || (contextNode != NULL)) { + if (compoundNext != NULL) { + /* + * This is a compound traversal. + */ + if (contextNode == NULL) { + /* + * Set the context for the initial traversal. + */ + compoundContextNode = inputList->nodeTab[inputIdx++]; + contextNode = compoundNext(NULL, compoundContextNode); + } else + contextNode = compoundNext(contextNode, compoundContextNode); + if (contextNode == NULL) + continue; + /* + * Set the context for the main traversal. + */ + xpctxt->node = contextNode; + } else + xpctxt->node = inputList->nodeTab[inputIdx++]; + + if (list == NULL) { + list = xmlXPathNodeSetCreate(NULL); + if (list == NULL) { + total = 0; + goto error; + } + } + cur = NULL; + specialNodeInSet = 0; do { cur = next(ctxt, cur); if (cur == NULL) break; - if ((first != NULL) && (*first == cur)) - break; - if (((t % 256) == 0) && - (first != NULL) && (*first != NULL) && - (xmlXPathCmpNodes(*first, cur) >= 0)) - break; - if ((last != NULL) && (*last == cur)) - break; - if (((t % 256) == 0) && - (last != NULL) && (*last != NULL) && - (xmlXPathCmpNodes(cur, *last) >= 0)) - break; - t++; + + if (first != NULL) { + if (*first == cur) + break; + if ((*first != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(*first, cur) >= 0)) +#else + (xmlXPathCmpNodes(*first, cur) >= 0)) +#endif + { + break; + } + } + if (last != NULL) { + if (*last == cur) + break; + if ((*last != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(cur, *last) >= 0)) +#else + (xmlXPathCmpNodes(cur, *last) >= 0)) +#endif + { + break; + } + } + + total++; #ifdef DEBUG_STEP xmlGenericError(xmlGenericErrorContext, " %s", cur->name); #endif switch (test) { - case NODE_TEST_NONE: - ctxt->context->node = tmp; - xmlXPathFreeObject(obj); - STRANGE return(t); + case NODE_TEST_NONE: + STRANGE + goto error; case NODE_TEST_TYPE: if ((cur->type == type) || ((type == NODE_TYPE_NODE) && @@ -9351,20 +11423,26 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, (cur->type == XML_CDATA_SECTION_NODE) || (cur->type == XML_TEXT_NODE))) || ((type == NODE_TYPE_TEXT) && - (cur->type == XML_CDATA_SECTION_NODE))) { + (cur->type == XML_CDATA_SECTION_NODE))) + { #ifdef DEBUG_STEP - n++; -#endif + nbMatches++; +#endif + if (cur->type == XML_NAMESPACE_DECL) + specialNodeInSet = 1; + /* + * TODO: Don't we need to use xmlXPathNodeSetAddNs() + * for namespace nodes here ? + */ addNode(list, cur); } break; case NODE_TEST_PI: - if (cur->type == XML_PI_NODE) { - if ((name != NULL) && - (!xmlStrEqual(name, cur->name))) - break; + if ((cur->type == XML_PI_NODE) && + ((name == NULL) || xmlStrEqual(name, cur->name))) + { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, cur); } @@ -9373,29 +11451,31 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, if (axis == AXIS_ATTRIBUTE) { if (cur->type == XML_ATTRIBUTE_NODE) { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, cur); } } else if (axis == AXIS_NAMESPACE) { if (cur->type == XML_NAMESPACE_DECL) { #ifdef DEBUG_STEP - n++; -#endif - xmlXPathNodeSetAddNs(list, ctxt->context->node, - (xmlNsPtr) cur); + nbMatches++; +#endif + specialNodeInSet = 1; + xmlXPathNodeSetAddNs(list, xpctxt->node, + (xmlNsPtr) cur); } } else { if (cur->type == XML_ELEMENT_NODE) { if (prefix == NULL) { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, cur); } else if ((cur->ns != NULL) && - (xmlStrEqual(URI, cur->ns->href))) { + (xmlStrEqual(URI, cur->ns->href))) + { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, cur); } @@ -9409,20 +11489,21 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, case NODE_TEST_NAME: switch (cur->type) { case XML_ELEMENT_NODE: - if (xmlStrEqual(name, cur->name)) { + if (xmlStrEqual(name, cur->name)) { if (prefix == NULL) { if (cur->ns == NULL) { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, cur); } } else { if ((cur->ns != NULL) && (xmlStrEqual(URI, - cur->ns->href))) { + cur->ns->href))) + { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, cur); } @@ -9437,7 +11518,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, if ((attr->ns == NULL) || (attr->ns->prefix == NULL)) { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, (xmlNodePtr) attr); @@ -9446,9 +11527,10 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, if ((attr->ns != NULL) && (xmlStrEqual(URI, attr->ns-> - href))) { + href))) + { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif addNode(list, (xmlNodePtr) attr); @@ -9462,21 +11544,22 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlNsPtr ns = (xmlNsPtr) cur; if ((ns->prefix != NULL) && (name != NULL) - && (xmlStrEqual(ns->prefix, name))) { + && (xmlStrEqual(ns->prefix, name))) + { #ifdef DEBUG_STEP - n++; + nbMatches++; #endif + specialNodeInSet = 1; xmlXPathNodeSetAddNs(list, - ctxt->context->node, (xmlNsPtr) cur); + xpctxt->node, (xmlNsPtr) cur); } } break; default: break; - } - break; - break; - } + } /* switch (cur->type) */ + break; /* case NODE_TEST_NAME: */ + } /* switch (test) */ } while (cur != NULL); /* @@ -9485,48 +11568,84 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0)) { xmlXPathObjectPtr obj2; - valuePush(ctxt, xmlXPathWrapNodeSet(list)); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, list)); xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]); CHECK_TYPE0(XPATH_NODESET); obj2 = valuePop(ctxt); list = obj2->nodesetval; obj2->nodesetval = NULL; - xmlXPathFreeObject(obj2); + xmlXPathReleaseObject(xpctxt, obj2); + if (ctxt->error != XPATH_EXPRESSION_OK) { - xmlXPathFreeObject(obj); - xmlXPathFreeNodeSet(list); - return(0); + total = 0; + goto error; } } - if (ret == NULL) { - ret = list; - } else { - ret = mergeNodeSet(ret, list); - xmlXPathFreeNodeSet(list); + if (resultList == NULL) { + resultList = list; + list = NULL; + } else if ((list != NULL) && (list->nodeNr > 0)) { + resultList = mergeNodeSet(resultList, list); + /* + * This is the list containing the current matching nodes. + * Avoid massive creation/freeing and preserve it for the + * next iterations. + */ + /* If a namespace node was put it, then we need a more + * time consuming cleanup. + */ + if (specialNodeInSet) + xmlXPathNodeSetClear(list); + else + list->nodeNr = 0; } } - ctxt->context->node = tmp; + + xpctxt->node = oldContextNode; + /* + * Cleanup the temporary list of current node-test matches. + */ + if ((list != NULL) && (list != resultList)) { + xmlXPathFreeNodeSet(list); + list = NULL; + } + #ifdef DEBUG_STEP xmlGenericError(xmlGenericErrorContext, "\nExamined %d nodes, found %d nodes at that step\n", - t, n); + total, nbMatches); #endif - valuePush(ctxt, xmlXPathWrapNodeSet(ret)); + + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, resultList)); + if ((obj->boolval) && (obj->user != NULL)) { + /* + * QUESTION TODO: What does this do and why? + */ ctxt->value->boolval = 1; ctxt->value->user = obj->user; obj->user = NULL; obj->boolval = 0; } - xmlXPathFreeObject(obj); - return(t); + xmlXPathReleaseObject(xpctxt, obj); + return(total); + +error: + xpctxt->node = oldContextNode; + xmlXPathReleaseObject(xpctxt, obj); + if ((list != NULL) && (list != resultList)) { + xmlXPathFreeNodeSet(list); + } + if (resultList != NULL) + xmlXPathFreeNodeSet(resultList); + return(total); } /** * xmlXPathNodeCollectAndTestNth: * @ctxt: the XPath Parser context * @op: the XPath precompiled step operation - * @indx: the index to collect + * @reqpos: the requested position wrt to the axis * @first: pointer to the first element in document order * @last: pointer to the last element in document order * @@ -9539,7 +11658,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, */ static int xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, - xmlXPathStepOpPtr op, int indx, + xmlXPathStepOpPtr op, int reqpos, xmlNodePtr * first, xmlNodePtr * last) { xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; @@ -9548,27 +11667,30 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, const xmlChar *prefix = op->value4; const xmlChar *name = op->value5; const xmlChar *URI = NULL; - int n = 0, t = 0; + int pos; /* The current context position */ - int i; - xmlNodeSetPtr list; + int inputIdx, total = 0; + xmlNodeSetPtr inputList, list; xmlXPathTraversalFunction next = NULL; + xmlXPathTraversalFunctionExt compoundNext = NULL; void (*addNode) (xmlNodeSetPtr, xmlNodePtr); - xmlNodePtr cur = NULL; + xmlNodePtr oldContextNode, contextNode, cur, compoundContextNode; xmlXPathObjectPtr obj; - xmlNodeSetPtr nodelist; - xmlNodePtr tmp; + xmlXPathContextPtr xpctxt = ctxt->context; + CHECK_TYPE0(XPATH_NODESET); obj = valuePop(ctxt); addNode = xmlXPathNodeSetAdd; + if (prefix != NULL) { - URI = xmlXPathNsLookup(ctxt->context, prefix); + URI = xmlXPathNsLookup(xpctxt, prefix); if (URI == NULL) { xmlXPathFreeObject(obj); XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); } } + #ifdef DEBUG_STEP_NTH xmlGenericError(xmlGenericErrorContext, "new step : "); if (first != NULL) { @@ -9615,7 +11737,20 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, xmlGenericError(xmlGenericErrorContext, "axis 'child' "); #endif last = NULL; - next = xmlXPathNextChild; + if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) { + /* + * This iterator will give us only nodes which can + * hold element nodes. + */ + compoundNext = xmlXPathNextDescendantOrSelfElemParent; + } + if ((test == NODE_TEST_NAME) && (type == NODE_TYPE_NODE)) { + /* + * Optimization if an element node type is 'element'. + */ + next = xmlXPathNextChildElement; + } else + next = xmlXPathNextChild; break; case AXIS_DESCENDANT: #ifdef DEBUG_STEP_NTH @@ -9687,17 +11822,17 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, break; } if (next == NULL) { - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(xpctxt, obj); return(0); } - nodelist = obj->nodesetval; - if (nodelist == NULL) { - xmlXPathFreeObject(obj); - valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); + inputList = obj->nodesetval; + if ((inputList == NULL) || (inputList->nodeNr <= 0)) { + xmlXPathReleaseObject(xpctxt, obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL)); return(0); } - addNode = xmlXPathNodeSetAddUnique; + #ifdef DEBUG_STEP_NTH xmlGenericError(xmlGenericErrorContext, " context contains %d nodes\n", nodelist->nodeNr); @@ -9743,34 +11878,78 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, * principal node type. For example, child::* will * select all element children of the context node */ - tmp = ctxt->context->node; + oldContextNode = xpctxt->node; + addNode = xmlXPathNodeSetAddUnique; + list = NULL; + compoundContextNode = NULL; + contextNode = NULL; + inputIdx = 0; list = xmlXPathNodeSetCreate(NULL); - for (i = 0; i < nodelist->nodeNr; i++) { - ctxt->context->node = nodelist->nodeTab[i]; + + while ((inputIdx < inputList->nodeNr) || (contextNode != NULL)) { + if (compoundNext != NULL) { + /* + * This is a compound traversal. + */ + if (contextNode == NULL) { + /* + * Set the context for the initial traversal. + */ + compoundContextNode = inputList->nodeTab[inputIdx++]; + contextNode = compoundNext(NULL, compoundContextNode); + } else + contextNode = compoundNext(contextNode, compoundContextNode); + if (contextNode == NULL) + continue; + /* + * Set the context for the main traversal. + */ + xpctxt->node = contextNode; + } else + xpctxt->node = inputList->nodeTab[inputIdx++]; cur = NULL; - n = 0; + pos = 0; do { cur = next(ctxt, cur); if (cur == NULL) break; - if ((first != NULL) && (*first == cur)) - break; - if (((t % 256) == 0) && - (first != NULL) && (*first != NULL) && - (xmlXPathCmpNodes(*first, cur) >= 0)) - break; - if ((last != NULL) && (*last == cur)) - break; - if (((t % 256) == 0) && - (last != NULL) && (*last != NULL) && - (xmlXPathCmpNodes(cur, *last) >= 0)) - break; - t++; + + if (first != NULL) { + if (*first == cur) + break; + if ((*first != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(*first, cur) >= 0)) +#else + (xmlXPathCmpNodes(*first, cur) >= 0)) +#endif + { + break; + } + } + if (last != NULL) { + if (*last == cur) + break; + if ((*last != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(cur, *last) >= 0)) +#else + (xmlXPathCmpNodes(cur, *last) >= 0)) +#endif + { + break; + } + } + + total++; switch (test) { case NODE_TEST_NONE: - ctxt->context->node = tmp; - STRANGE return(0); + total = 0; + STRANGE + goto error; case NODE_TEST_TYPE: if ((cur->type == type) || ((type == NODE_TYPE_NODE) && @@ -9783,8 +11962,8 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, (cur->type == XML_TEXT_NODE))) || ((type == NODE_TYPE_TEXT) && (cur->type == XML_CDATA_SECTION_NODE))) { - n++; - if (n == indx) + pos++; + if (pos == reqpos) addNode(list, cur); } break; @@ -9793,35 +11972,35 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, if ((name != NULL) && (!xmlStrEqual(name, cur->name))) break; - n++; - if (n == indx) + pos++; + if (pos == reqpos) addNode(list, cur); } break; case NODE_TEST_ALL: if (axis == AXIS_ATTRIBUTE) { if (cur->type == XML_ATTRIBUTE_NODE) { - n++; - if (n == indx) + pos++; + if (pos == reqpos) addNode(list, cur); } } else if (axis == AXIS_NAMESPACE) { if (cur->type == XML_NAMESPACE_DECL) { - n++; - if (n == indx) - xmlXPathNodeSetAddNs(list, ctxt->context->node, + pos++; + if (pos == reqpos) + xmlXPathNodeSetAddNs(list, xpctxt->node, (xmlNsPtr) cur); } } else { if (cur->type == XML_ELEMENT_NODE) { if (prefix == NULL) { - n++; - if (n == indx) + pos++; + if (pos == reqpos) addNode(list, cur); } else if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) { - n++; - if (n == indx) + pos++; + if (pos == reqpos) addNode(list, cur); } } @@ -9837,16 +12016,17 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, if (xmlStrEqual(name, cur->name)) { if (prefix == NULL) { if (cur->ns == NULL) { - n++; - if (n == indx) + pos++; + if (pos == reqpos) addNode(list, cur); } } else { if ((cur->ns != NULL) && (xmlStrEqual(URI, - cur->ns->href))) { - n++; - if (n == indx) + cur->ns->href))) + { + pos++; + if (pos == reqpos) addNode(list, cur); } } @@ -9858,18 +12038,19 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, if (xmlStrEqual(name, attr->name)) { if (prefix == NULL) { if ((attr->ns == NULL) || - (attr->ns->prefix == NULL)) { - n++; - if (n == indx) + (attr->ns->prefix == NULL)) + { + pos++; + if (pos == reqpos) addNode(list, cur); } } else { if ((attr->ns != NULL) && (xmlStrEqual(URI, - attr->ns-> - href))) { - n++; - if (n == indx) + attr->ns->href))) + { + pos++; + if (pos == reqpos) addNode(list, cur); } } @@ -9882,10 +12063,10 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, if ((ns->prefix != NULL) && (name != NULL) && (xmlStrEqual(ns->prefix, name))) { - n++; - if (n == indx) + pos++; + if (pos == reqpos) xmlXPathNodeSetAddNs(list, - ctxt->context->node, (xmlNsPtr) cur); + xpctxt->node, (xmlNsPtr) cur); } } break; @@ -9893,27 +12074,40 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, break; } break; - break; } - } while (n < indx); + } while (pos < reqpos); } - ctxt->context->node = tmp; + xpctxt->node = oldContextNode; + #ifdef DEBUG_STEP_NTH xmlGenericError(xmlGenericErrorContext, "\nExamined %d nodes, found %d nodes at that step\n", - t, list->nodeNr); + total, list->nodeNr); #endif - valuePush(ctxt, xmlXPathWrapNodeSet(list)); + + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, list)); + if ((obj->boolval) && (obj->user != NULL)) { ctxt->value->boolval = 1; ctxt->value->user = obj->user; obj->user = NULL; obj->boolval = 0; } - xmlXPathFreeObject(obj); - return(t); + xmlXPathReleaseObject(xpctxt, obj); + return(total); + +error: + xpctxt->node = oldContextNode; + xmlXPathReleaseObject(xpctxt, obj); + if (list != NULL) + xmlXPathFreeNodeSet(list); + return(total); } +static int +xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, xmlNodePtr * first); + /** * xmlXPathCompOpEvalFirst: * @ctxt: the XPath parser context with the compiled expression @@ -9950,7 +12144,15 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, /* * limit tree traversing to first node in the result */ - xmlXPathNodeSetSort(ctxt->value->nodesetval); + /* + * OPTIMIZE TODO: This implicitely sorts + * the result, even if not needed. E.g. if the argument + * of the count() function, no sorting is needed. + * OPTIMIZE TODO: How do we know if the node-list wasn't + * aready sorted? + */ + if (ctxt->value->nodesetval->nodeNr > 1) + xmlXPathNodeSetSort(ctxt->value->nodesetval); *first = ctxt->value->nodesetval->nodeTab[0]; } cur = @@ -9966,7 +12168,7 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, arg2->nodesetval); valuePush(ctxt, arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); /* optimizer */ if (total > cur) xmlXPathCompSwap(op); @@ -9981,7 +12183,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, if (op->ch2 != -1) total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); CHECK_ERROR0; - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); return (total); case XPATH_OP_RESET: if (op->ch1 != -1) @@ -10026,7 +12229,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, } case XPATH_OP_VALUE: valuePush(ctxt, - xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); + xmlXPathCacheObjectCopy(ctxt->context, + (xmlXPathObjectPtr) op->value4)); return (0); case XPATH_OP_SORT: if (op->ch1 != -1) @@ -10036,9 +12240,15 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, CHECK_ERROR0; if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET) - && (ctxt->value->nodesetval != NULL)) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr > 1)) xmlXPathNodeSetSort(ctxt->value->nodesetval); return (total); +#ifdef XP_OPTIMIZED_FILTER_FIRST + case XPATH_OP_FILTER: + total =+ xmlXPathCompOpEvalFilterFirst(ctxt, op, first); + return (total); +#endif default: return (xmlXPathCompOpEval(ctxt, op)); } @@ -10087,7 +12297,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, /* * limit tree traversing to first node in the result */ - xmlXPathNodeSetSort(ctxt->value->nodesetval); + if (ctxt->value->nodesetval->nodeNr > 1) + xmlXPathNodeSetSort(ctxt->value->nodesetval); *last = ctxt->value->nodesetval->nodeTab[ctxt->value-> nodesetval->nodeNr - @@ -10103,7 +12314,7 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET) && (ctxt->value->nodesetval != NULL) - && (ctxt->value->nodesetval->nodeNr >= 1)) { + && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */ } CHECK_TYPE0(XPATH_NODESET); arg2 = valuePop(ctxt); @@ -10114,7 +12325,7 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, arg2->nodesetval); valuePush(ctxt, arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); /* optimizer */ if (total > cur) xmlXPathCompSwap(op); @@ -10129,7 +12340,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, if (op->ch2 != -1) total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); CHECK_ERROR0; - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); return (total); case XPATH_OP_RESET: if (op->ch1 != -1) @@ -10176,7 +12388,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, } case XPATH_OP_VALUE: valuePush(ctxt, - xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); + xmlXPathCacheObjectCopy(ctxt->context, + (xmlXPathObjectPtr) op->value4)); return (0); case XPATH_OP_SORT: if (op->ch1 != -1) @@ -10186,7 +12399,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, CHECK_ERROR0; if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET) - && (ctxt->value->nodesetval != NULL)) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr > 1)) xmlXPathNodeSetSort(ctxt->value->nodesetval); return (total); default: @@ -10194,6 +12408,299 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, } } +#ifdef XP_OPTIMIZED_FILTER_FIRST +static int +xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, xmlNodePtr * first) +{ + int total = 0; + xmlXPathCompExprPtr comp; + xmlXPathObjectPtr res; + xmlXPathObjectPtr obj; + xmlNodeSetPtr oldset; + xmlNodePtr oldnode; + xmlDocPtr oldDoc; + int i; + + CHECK_ERROR0; + comp = ctxt->comp; + /* + * Optimization for ()[last()] selection i.e. the last elem + */ + if ((op->ch1 != -1) && (op->ch2 != -1) && + (comp->steps[op->ch1].op == XPATH_OP_SORT) && + (comp->steps[op->ch2].op == XPATH_OP_SORT)) { + int f = comp->steps[op->ch2].ch1; + + if ((f != -1) && + (comp->steps[f].op == XPATH_OP_FUNCTION) && + (comp->steps[f].value5 == NULL) && + (comp->steps[f].value == 0) && + (comp->steps[f].value4 != NULL) && + (xmlStrEqual + (comp->steps[f].value4, BAD_CAST "last"))) { + xmlNodePtr last = NULL; + + total += + xmlXPathCompOpEvalLast(ctxt, + &comp->steps[op->ch1], + &last); + CHECK_ERROR0; + /* + * The nodeset should be in document order, + * Keep only the last value + */ + if ((ctxt->value != NULL) && + (ctxt->value->type == XPATH_NODESET) && + (ctxt->value->nodesetval != NULL) && + (ctxt->value->nodesetval->nodeTab != NULL) && + (ctxt->value->nodesetval->nodeNr > 1)) { + ctxt->value->nodesetval->nodeTab[0] = + ctxt->value->nodesetval->nodeTab[ctxt-> + value-> + nodesetval-> + nodeNr - + 1]; + ctxt->value->nodesetval->nodeNr = 1; + *first = *(ctxt->value->nodesetval->nodeTab); + } + return (total); + } + } + + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 == -1) + return (total); + if (ctxt->value == NULL) + return (total); + +#ifdef LIBXML_XPTR_ENABLED + oldnode = ctxt->context->node; + /* + * 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); + obj = valuePop(ctxt); + oldlocset = obj->user; + ctxt->context->node = NULL; + + if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + res = valuePop(ctxt); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + valuePush(ctxt, obj); + CHECK_ERROR0; + return (total); + } + 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; + if (tmp == NULL) { + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + } else { + xmlXPathNodeSetAddUnique(tmp->nodesetval, + ctxt->context->node); + } + valuePush(ctxt, tmp); + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(obj); + return(0); + } + /* + * 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); + /* + * REVISIT TODO: Don't create a temporary nodeset + * for everly iteration. + */ + /* OLD: xmlXPathFreeObject(res); */ + } else + tmp = NULL; + ctxt->context->node = 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. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); + ctxt->context->node = oldnode; + 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); + obj = valuePop(ctxt); + oldset = obj->nodesetval; + + oldnode = ctxt->context->node; + oldDoc = ctxt->context->doc; + ctxt->context->node = NULL; + + if ((oldset == NULL) || (oldset->nodeNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + /* QUESTION TODO: Why was this code commented out? + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + CHECK_ERROR0; + res = valuePop(ctxt); + if (res != NULL) + xmlXPathFreeObject(res); + */ + valuePush(ctxt, obj); + ctxt->context->node = oldnode; + CHECK_ERROR0; + } else { + xmlNodeSetPtr newset; + xmlXPathObjectPtr 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); + + 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 { + xmlXPathNodeSetAddUnique(tmp->nodesetval, + ctxt->context->node); + } + 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); + xmlXPathFreeObject(obj); + return(0); + } + /* + * The result of the evaluation needs to be tested to + * decide whether the filter succeeded or not + */ + res = valuePop(ctxt); + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); + } + /* + * 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); + } else + tmp = NULL; + ctxt->context->node = 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. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + /* may want to move this past the '}' later */ + ctxt->context->doc = oldDoc; + valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, newset)); + } + ctxt->context->node = oldnode; + return(total); +} +#endif /* XP_OPTIMIZED_FILTER_FIRST */ + /** * xmlXPathCompOpEval: * @ctxt: the XPath parser context with the compiled expression @@ -10243,7 +12750,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) arg1 = valuePop(ctxt); arg1->boolval &= arg2->boolval; valuePush(ctxt, arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); return (total); case XPATH_OP_OR: bakd = ctxt->context->doc; @@ -10269,7 +12776,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) arg1 = valuePop(ctxt); arg1->boolval |= arg2->boolval; valuePush(ctxt, arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); return (total); case XPATH_OP_EQUAL: bakd = ctxt->context->doc; @@ -10288,7 +12795,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) equal = xmlXPathEqualValues(ctxt); else equal = xmlXPathNotEqualValues(ctxt); - valuePush(ctxt, xmlXPathNewBoolean(equal)); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal)); return (total); case XPATH_OP_CMP: bakd = ctxt->context->doc; @@ -10304,7 +12811,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); CHECK_ERROR0; ret = xmlXPathCompareValues(ctxt, op->value, op->value2); - valuePush(ctxt, xmlXPathNewBoolean(ret)); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret)); return (total); case XPATH_OP_PLUS: bakd = ctxt->context->doc; @@ -10371,10 +12878,16 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) CHECK_TYPE0(XPATH_NODESET); arg1 = valuePop(ctxt); - arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, - arg2->nodesetval); + if ((arg1->nodesetval == NULL) || + ((arg2->nodesetval != NULL) && + (arg2->nodesetval->nodeNr != 0))) + { + arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, + arg2->nodesetval); + } + valuePush(ctxt, arg1); - xmlXPathFreeObject(arg2); + xmlXPathReleaseObject(ctxt->context, arg2); return (total); case XPATH_OP_ROOT: xmlXPathRoot(ctxt); @@ -10386,7 +12899,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) if (op->ch2 != -1) total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); CHECK_ERROR0; - valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); return (total); case XPATH_OP_RESET: if (op->ch1 != -1) @@ -10403,18 +12917,18 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); CHECK_ERROR0; - - /* - * Optimization for [n] selection where n is a number - */ - if ((op->ch2 != -1) && + + if ((op->ch2 != -1) && (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && (comp->steps[op->ch2].ch1 == -1) && (comp->steps[op->ch2].ch2 != -1) && (comp->steps[comp->steps[op->ch2].ch2].op == - XPATH_OP_VALUE)) { + XPATH_OP_VALUE)) + { xmlXPathObjectPtr val; - + /* + * Optimization for [n] selection where n is a number + */ val = comp->steps[comp->steps[op->ch2].ch2].value4; if ((val != NULL) && (val->type == XPATH_NUMBER)) { int indx = (int) val->floatval; @@ -10433,7 +12947,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) } case XPATH_OP_VALUE: valuePush(ctxt, - xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); + xmlXPathCacheObjectCopy(ctxt->context, + (xmlXPathObjectPtr) op->value4)); return (total); case XPATH_OP_VARIABLE:{ xmlXPathObjectPtr val; @@ -10560,8 +13075,22 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) * Optimization for ()[1] selection i.e. the first elem */ if ((op->ch1 != -1) && (op->ch2 != -1) && - (comp->steps[op->ch1].op == XPATH_OP_SORT) && - (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { +#ifdef XP_OPTIMIZED_FILTER_FIRST + /* + * FILTER TODO: Can we assume that the inner processing + * will result in an ordered list if we have an + * XPATH_OP_FILTER? + * What about an additional field or flag on + * xmlXPathObject like @sorted ? This way we wouln'd need + * to assume anything, so it would be more robust and + * easier to optimize. + */ + ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */ + (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */ +#else + (comp->steps[op->ch1].op == XPATH_OP_SORT) && +#endif + (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */ xmlXPathObjectPtr val; val = comp->steps[op->ch2].value4; @@ -10628,7 +13157,17 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) return (total); } } - + /* + * Process inner predicates first. + * Example "index[parent::book][1]": + * ... + * PREDICATE <-- we are here "[1]" + * PREDICATE <-- process "[parent::book]" first + * SORT + * COLLECT 'parent' 'name' 'node' book + * NODE + * ELEM Object is a number : 1 + */ if (op->ch1 != -1) total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); @@ -10666,8 +13205,9 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); res = valuePop(ctxt); - if (res != NULL) - xmlXPathFreeObject(res); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } valuePush(ctxt, obj); CHECK_ERROR0; return (total); @@ -10682,9 +13222,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) ctxt->context->node = oldlocset->locTab[i]->user; ctxt->context->contextSize = oldlocset->locNr; ctxt->context->proximityPosition = i + 1; - tmp = xmlXPathNewNodeSet(ctxt->context->node); + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); valuePush(ctxt, tmp); - + if (op->ch2 != -1) total += xmlXPathCompOpEval(ctxt, @@ -10708,11 +13249,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) /* * Cleanup */ - if (res != NULL) - xmlXPathFreeObject(res); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } if (ctxt->value == tmp) { res = valuePop(ctxt); - xmlXPathFreeObject(res); + xmlXPathReleaseObject(ctxt->context, res); } ctxt->context->node = NULL; @@ -10721,7 +13263,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) /* * The result is used as the new evaluation locset. */ - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); ctxt->context->node = NULL; ctxt->context->contextSize = -1; ctxt->context->proximityPosition = -1; @@ -10761,13 +13303,39 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) ctxt->context->node = oldnode; CHECK_ERROR0; } else { + 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 @@ -10777,11 +13345,21 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) && (oldset->nodeTab[i]->doc != NULL)) ctxt->context->doc = oldset->nodeTab[i]->doc; - tmp = xmlXPathNewNodeSet(ctxt->context->node); + if (tmp == NULL) { + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + } else { + xmlXPathNodeSetAddUnique(tmp->nodesetval, + ctxt->context->node); + } 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, @@ -10796,6 +13374,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) * 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)) { xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); @@ -10804,26 +13386,34 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) /* * Cleanup */ - if (res != NULL) - xmlXPathFreeObject(res); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } if (ctxt->value == tmp) { - res = valuePop(ctxt); - xmlXPathFreeObject(res); - } - + valuePop(ctxt); + xmlXPathNodeSetClear(tmp->nodesetval); + /* + * Don't free the temporary nodeset + * in order to avoid massive recreation inside this + * loop. + */ + } else + tmp = NULL; ctxt->context->node = NULL; } - + if (tmp != NULL) + xmlXPathReleaseObject(ctxt->context, tmp); /* * The result is used as the new evaluation set. */ - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); ctxt->context->node = NULL; ctxt->context->contextSize = -1; ctxt->context->proximityPosition = -1; /* may want to move this past the '}' later */ ctxt->context->doc = oldDoc; - valuePush(ctxt, xmlXPathWrapNodeSet(newset)); + valuePush(ctxt, + xmlXPathCacheWrapNodeSet(ctxt->context, newset)); } ctxt->context->node = oldnode; return (total); @@ -10834,8 +13424,11 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) CHECK_ERROR0; if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET) && - (ctxt->value->nodesetval != NULL)) + (ctxt->value->nodesetval != NULL) && + (ctxt->value->nodesetval->nodeNr > 1)) + { xmlXPathNodeSetSort(ctxt->value->nodesetval); + } return (total); #ifdef LIBXML_XPTR_ENABLED case XPATH_OP_RANGETO:{ @@ -10869,8 +13462,9 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) ctxt->context->proximityPosition = 0; total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]); res = valuePop(ctxt); - if (res != NULL) - xmlXPathFreeObject(res); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } valuePush(ctxt, obj); CHECK_ERROR0; return (total); @@ -10885,7 +13479,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) ctxt->context->node = oldlocset->locTab[i]->user; ctxt->context->contextSize = oldlocset->locNr; ctxt->context->proximityPosition = i + 1; - tmp = xmlXPathNewNodeSet(ctxt->context->node); + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); valuePush(ctxt, tmp); if (op->ch2 != -1) @@ -10922,11 +13517,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) /* * Cleanup */ - if (res != NULL) - xmlXPathFreeObject(res); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } if (ctxt->value == tmp) { res = valuePop(ctxt); - xmlXPathFreeObject(res); + xmlXPathReleaseObject(ctxt->context, res); } ctxt->context->node = NULL; @@ -10946,7 +13542,11 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) * in the nodeset. */ ctxt->context->node = oldset->nodeTab[i]; - tmp = xmlXPathNewNodeSet(ctxt->context->node); + /* + * OPTIMIZE TODO: Avoid recreation for every iteration. + */ + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); valuePush(ctxt, tmp); if (op->ch2 != -1) @@ -10969,11 +13569,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) /* * Cleanup */ - if (res != NULL) - xmlXPathFreeObject(res); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } if (ctxt->value == tmp) { res = valuePop(ctxt); - xmlXPathFreeObject(res); + xmlXPathReleaseObject(ctxt->context, res); } ctxt->context->node = NULL; @@ -10984,7 +13585,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) /* * The result is used as the new evaluation set. */ - xmlXPathFreeObject(obj); + xmlXPathReleaseObject(ctxt->context, obj); ctxt->context->node = NULL; ctxt->context->contextSize = -1; ctxt->context->proximityPosition = -1; @@ -11036,7 +13637,7 @@ xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp) { printf("stream eval: depth %d from root %d\n", max_depth, from_root); #endif - retval = xmlXPathNewNodeSet(NULL); + retval = xmlXPathCacheNewNodeSet(ctxt, NULL); if (retval == NULL) return(NULL); @@ -11378,12 +13979,18 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { const xmlChar *tmp; /* - * We don't try to handle :: constructs, just the simplied form at - * this point + * We don't try to handle expressions using the verbose axis + * specifiers ("::"), just the simplied form at this point. + * Additionally, if there is no list of namespaces available and + * there's a ":" in the expression, indicating a prefixed QName, + * then we won't try to compile either. xmlPatterncompile() needs + * to have a list of namespaces at compilation time in order to + * compile prefixed name tests. */ tmp = xmlStrchr(str, ':'); - if ((tmp != NULL) && (tmp[1] == ':')) - return(NULL); + if ((tmp != NULL) && + ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':'))) + return(NULL); if (ctxt != NULL) { dict = ctxt->dict; @@ -11405,6 +14012,9 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH, &namespaces[0]); + if (namespaces != NULL) { + xmlFree((xmlChar **)namespaces); + } if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) { comp = xmlXPathNewCompExpr(); if (comp == NULL) { @@ -11423,6 +14033,59 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { } #endif /* XPATH_STREAMING */ +static int +xmlXPathCanRewriteDosExpression(xmlChar *expr) +{ + if (expr == NULL) + return(0); + do { + if ((*expr == '/') && (*(++expr) == '/')) + return(1); + } while (*expr++); + return(0); +} +static void +xmlXPathRewriteDOSExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op) +{ + /* + * Try to rewrite "descendant-or-self::node()/foo" to an optimized + * internal representation. + */ + if (op->ch1 != -1) { + if ((op->op == XPATH_OP_COLLECT /* 11 */) && + ((xmlXPathAxisVal) op->value == AXIS_CHILD /* 4 */) && + ((xmlXPathTestVal) op->value2 == NODE_TEST_NAME /* 5 */) && + ((xmlXPathTypeVal) op->value3 == NODE_TYPE_NODE /* 0 */)) + { + /* + * This is an "foo" + */ + xmlXPathStepOpPtr prevop = &comp->steps[op->ch1]; + + if ((prevop->op == XPATH_OP_COLLECT /* 11 */) && + (prevop->ch1 != -1) && + ((xmlXPathAxisVal) prevop->value == + AXIS_DESCENDANT_OR_SELF) && + (prevop->ch2 == -1) && + ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) && + ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE) && + (comp->steps[prevop->ch1].op == XPATH_OP_ROOT)) + { + /* + * This is a "descendant-or-self::node()" without predicates. + * Eliminate it. + */ + op->ch1 = prevop->ch1; + op->rewriteType = XP_REWRITE_DOS_CHILD_ELEM; + } + } + if (op->ch1 != -1) + xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch1]); + } + if (op->ch2 != -1) + xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch2]); +} + /** * xmlXPathCtxtCompile: * @ctxt: an XPath context @@ -11447,7 +14110,7 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { xmlXPathInit(); pctxt = xmlXPathNewParserContext(str, ctxt); - xmlXPathCompileExpr(pctxt); + xmlXPathCompileExpr(pctxt, 1); if( pctxt->error != XPATH_EXPRESSION_OK ) { @@ -11474,7 +14137,12 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { #ifdef DEBUG_EVAL_COUNTS comp->string = xmlStrdup(str); comp->nb = 0; -#endif +#endif + if ((comp->nbStep > 2) && + (xmlXPathCanRewriteDosExpression(comp->expr) == 1)) + { + xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]); + }
} return(comp); } @@ -11547,8 +14215,8 @@ xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) { tmp = valuePop(ctxt); if (tmp != NULL) { if (tmp != init) - stack++; - xmlXPathFreeObject(tmp); + stack++; + xmlXPathReleaseObject(ctx, tmp); } } while (tmp != NULL); if ((stack != 0) && (res != NULL)) { @@ -11559,9 +14227,7 @@ xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) { if (ctxt->error != XPATH_EXPRESSION_OK) { xmlXPathFreeObject(res); res = NULL; - } - - + } ctxt->comp = NULL; xmlXPathFreeParserContext(ctxt); #ifndef LIBXML_THREAD_ENABLED @@ -11596,10 +14262,17 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { } else #endif { - xmlXPathCompileExpr(ctxt); + xmlXPathCompileExpr(ctxt, 1); + if ((ctxt->comp != NULL) &&
+ (ctxt->comp->nbStep > 2) && + (xmlXPathCanRewriteDosExpression(ctxt->comp->expr) == 1)) + { + xmlXPathRewriteDOSExpression(ctxt->comp, + &ctxt->comp->steps[comp->last]); + } } CHECK_ERROR; - xmlXPathRunEval(ctxt); + xmlXPathRunEval(ctxt); } /** @@ -11644,8 +14317,8 @@ xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) { tmp = valuePop(ctxt); if (tmp != NULL) { if (tmp != init) - stack++; - xmlXPathFreeObject(tmp); + stack++; + xmlXPathReleaseObject(ctx, tmp); } } while (tmp != NULL); if ((stack != 0) && (res != NULL)) { @@ -11694,7 +14367,7 @@ xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) { do { tmp = valuePop(pctxt); if (tmp != NULL) { - xmlXPathFreeObject(tmp); + xmlXPathReleaseObject(ctxt, tmp); stack++; } } while (tmp != NULL); @@ -11815,9 +14488,10 @@ xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { } } } - valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); xmlBufferFree(target); - xmlXPathFreeObject(str); + xmlXPathReleaseObject(ctxt->context, str); } /** |