diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f13e8f575..5cd687269 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -12,9 +12,9 @@ jobs:
fail-fast: false
matrix:
node-version:
+ - 22
- 20
- 18
- - 16
steps:
- uses: actions/checkout@v2
with:
diff --git a/src/expressions/functions/argumentHelper.ts b/src/expressions/functions/argumentHelper.ts
index 1d521fab8..a13bf1f0a 100644
--- a/src/expressions/functions/argumentHelper.ts
+++ b/src/expressions/functions/argumentHelper.ts
@@ -3,6 +3,7 @@ import castToType from '../dataTypes/castToType';
import ISequence from '../dataTypes/ISequence';
import isSubtypeOf from '../dataTypes/isSubtypeOf';
import promoteToType from '../dataTypes/promoteToType';
+import sequenceFactory from '../dataTypes/sequenceFactory';
import Value, {
SequenceMultiplicity,
SequenceType,
@@ -10,7 +11,11 @@ import Value, {
ValueType,
valueTypeToString,
} from '../dataTypes/Value';
+import DynamicContext from '../DynamicContext';
import ExecutionParameters from '../ExecutionParameters';
+import StaticContext from '../StaticContext';
+import { errXPDY0002 } from '../XPathErrors';
+import FunctionDefinitionType from './FunctionDefinitionType';
function mapItem(
argumentItem: Value,
@@ -149,3 +154,31 @@ export const performFunctionConversion = (
},
});
};
+
+export function contextItemAsFirstArgument(
+ functionName: string,
+ parameterType: ValueType,
+ fn: FunctionDefinitionType,
+): FunctionDefinitionType {
+ return (
+ dynamicContext: DynamicContext,
+ executionParameters: ExecutionParameters,
+ staticContext: StaticContext,
+ ): ISequence => {
+ if (dynamicContext.contextItem === null) {
+ throw errXPDY0002(
+ `The function ${functionName} depends on dynamic context, which is absent.`,
+ );
+ }
+
+ const contextItemArgument = performFunctionConversion(
+ { type: parameterType, mult: SequenceMultiplicity.EXACTLY_ONE },
+ sequenceFactory.singleton(dynamicContext.contextItem),
+ executionParameters,
+ functionName,
+ false,
+ );
+
+ return fn(dynamicContext, executionParameters, staticContext, contextItemArgument);
+ };
+}
diff --git a/src/expressions/functions/builtInFunctions_node.ts b/src/expressions/functions/builtInFunctions_node.ts
index 420068313..8f4be779e 100644
--- a/src/expressions/functions/builtInFunctions_node.ts
+++ b/src/expressions/functions/builtInFunctions_node.ts
@@ -19,40 +19,18 @@ import isSubtypeOf from '../dataTypes/isSubtypeOf';
import sequenceFactory from '../dataTypes/sequenceFactory';
import Value, { SequenceMultiplicity, ValueType } from '../dataTypes/Value';
import QName from '../dataTypes/valueTypes/QName';
-import DynamicContext from '../DynamicContext';
-import ExecutionParameters from '../ExecutionParameters';
import arePointersEqual from '../operators/compares/arePointersEqual';
import { BUILT_IN_NAMESPACE_URIS } from '../staticallyKnownNamespaces';
-import StaticContext from '../StaticContext';
import { IterationHint } from '../util/iterators';
import zipSingleton from '../util/zipSingleton';
import { errXPDY0002 } from '../XPathErrors';
+import { contextItemAsFirstArgument } from './argumentHelper';
import { BuiltinDeclarationType } from './builtInFunctions';
import builtinStringFunctions from './builtInFunctions_string';
import FunctionDefinitionType from './FunctionDefinitionType';
import generateId from './generateId';
const fnString = builtinStringFunctions.functions.string;
-function contextItemAsFirstArgument(
- functionName: string,
- fn: FunctionDefinitionType,
- dynamicContext: DynamicContext,
- executionParameters: ExecutionParameters,
- staticContext: StaticContext,
-) {
- if (dynamicContext.contextItem === null) {
- throw errXPDY0002(
- `The function ${functionName} depends on dynamic context, which is absent.`,
- );
- }
- return fn(
- dynamicContext,
- executionParameters,
- staticContext,
- sequenceFactory.singleton(dynamicContext.contextItem),
- );
-}
-
const fnNodeName: FunctionDefinitionType = (
_dynamicContext,
executionParameters,
@@ -499,7 +477,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'name', fnName),
+ callFunction: contextItemAsFirstArgument('name', ValueType.NODE, fnName),
localName: 'name',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSSTRING, mult: SequenceMultiplicity.EXACTLY_ONE },
@@ -515,7 +493,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'namespace-uri', fnNamespaceURI),
+ callFunction: contextItemAsFirstArgument('namespace-uri', ValueType.NODE, fnNamespaceURI),
localName: 'namespace-uri',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSANYURI, mult: SequenceMultiplicity.EXACTLY_ONE },
@@ -550,7 +528,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'has-children', fnHasChildren),
+ callFunction: contextItemAsFirstArgument('has-children', ValueType.NODE, fnHasChildren),
localName: 'has-children',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSBOOLEAN, mult: SequenceMultiplicity.EXACTLY_ONE },
@@ -566,7 +544,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'path', fnPath),
+ callFunction: contextItemAsFirstArgument('path', ValueType.NODE, fnPath),
localName: 'path',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSSTRING, mult: SequenceMultiplicity.ZERO_OR_ONE },
@@ -582,7 +560,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'node-name', fnNodeName),
+ callFunction: contextItemAsFirstArgument('node-name', ValueType.NODE, fnNodeName),
localName: 'node-name',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSQNAME, mult: SequenceMultiplicity.ZERO_OR_ONE },
@@ -598,7 +576,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'local-name', fnLocalName),
+ callFunction: contextItemAsFirstArgument('local-name', ValueType.NODE, fnLocalName),
localName: 'local-name',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSSTRING, mult: SequenceMultiplicity.EXACTLY_ONE },
@@ -614,7 +592,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'root', fnRoot),
+ callFunction: contextItemAsFirstArgument('root', ValueType.NODE, fnRoot),
localName: 'root',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.NODE, mult: SequenceMultiplicity.ZERO_OR_ONE },
@@ -622,7 +600,7 @@ const declarations: BuiltinDeclarationType[] = [
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'data', fnData),
+ callFunction: contextItemAsFirstArgument('data', ValueType.ITEM, fnData),
localName: 'data',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSANYATOMICTYPE, mult: SequenceMultiplicity.ZERO_OR_MORE },
@@ -654,7 +632,7 @@ const declarations: BuiltinDeclarationType[] = [
},
{
argumentTypes: [],
- callFunction: contextItemAsFirstArgument.bind(null, 'generate-id', fnGenerateId),
+ callFunction: contextItemAsFirstArgument('generate-id', ValueType.NODE, fnGenerateId),
localName: 'generate-id',
namespaceURI: BUILT_IN_NAMESPACE_URIS.FUNCTIONS_NAMESPACE_URI,
returnType: { type: ValueType.XSSTRING, mult: SequenceMultiplicity.EXACTLY_ONE },
diff --git a/src/expressions/functions/builtInFunctions_string.ts b/src/expressions/functions/builtInFunctions_string.ts
index 291c775be..5e7d4f859 100644
--- a/src/expressions/functions/builtInFunctions_string.ts
+++ b/src/expressions/functions/builtInFunctions_string.ts
@@ -16,6 +16,7 @@ import { errXPDY0002 } from '../XPathErrors';
import { BuiltinDeclarationType } from './builtInFunctions';
import builtInNumericFunctions from './builtInFunctions_numeric';
import FunctionDefinitionType from './FunctionDefinitionType';
+import { contextItemAsFirstArgument } from './argumentHelper';
const fnRound = builtInNumericFunctions.functions.round;
@@ -23,25 +24,6 @@ function collationError(): ISequence {
throw new Error('FOCH0002: No collations are supported');
}
-function contextItemAsFirstArgument(
- fn: FunctionDefinitionType,
- dynamicContext: DynamicContext,
- executionParameters: ExecutionParameters,
- staticContext: StaticContext,
-) {
- if (dynamicContext.contextItem === null) {
- throw errXPDY0002(
- 'The function which was called depends on dynamic context, which is absent.',
- );
- }
- return fn(
- dynamicContext,
- executionParameters,
- staticContext,
- sequenceFactory.singleton(dynamicContext.contextItem),
- );
-}
-
const fnCompare: FunctionDefinitionType = (
_dynamicContext,
_executionParameters,
@@ -767,8 +749,9 @@ const declarations: BuiltinDeclarationType[] = [
localName: 'normalize-space',
argumentTypes: [],
returnType: { type: ValueType.XSSTRING, mult: SequenceMultiplicity.EXACTLY_ONE },
- callFunction: contextItemAsFirstArgument.bind(
- null,
+ callFunction: contextItemAsFirstArgument(
+ 'normalize-space',
+ ValueType.XSSTRING,
(
dynamicContext: DynamicContext,
executionParameters: ExecutionParameters,
@@ -820,7 +803,7 @@ const declarations: BuiltinDeclarationType[] = [
localName: 'string',
argumentTypes: [],
returnType: { type: ValueType.XSSTRING, mult: SequenceMultiplicity.EXACTLY_ONE },
- callFunction: contextItemAsFirstArgument.bind(null, fnString),
+ callFunction: contextItemAsFirstArgument('string', ValueType.ITEM, fnString),
},
{
@@ -926,8 +909,11 @@ const declarations: BuiltinDeclarationType[] = [
localName: 'string-length',
argumentTypes: [],
returnType: { type: ValueType.XSINTEGER, mult: SequenceMultiplicity.EXACTLY_ONE },
- callFunction: contextItemAsFirstArgument.bind(
- null,
+ callFunction: contextItemAsFirstArgument(
+ 'string-length',
+ // Note: deviate from the spec to allow expressions line `1!fn:string-length()` to
+ // atomize the input still
+ ValueType.XSANYATOMICTYPE,
(
dynamicContext: DynamicContext,
executionParameters: ExecutionParameters,
diff --git a/test/assets/unrunnableTestCases.csv b/test/assets/unrunnableTestCases.csv
index 3dcc816c4..80dc07251 100644
--- a/test/assets/unrunnableTestCases.csv
+++ b/test/assets/unrunnableTestCases.csv
@@ -726,11 +726,8 @@ generate-id-015,Error: No selector counterpart for: computedDocumentConstructor.
generate-id-016,Error: No selector counterpart for: computedDocumentConstructor.
generate-id-017,Error: No selector counterpart for: computedDocumentConstructor.
generate-id-018,Error: XPTY0004: Multiplicity of function argument of type node()? for generate-id is incorrect. Expected "?", but got "+".
-fn-has-children-007,AssertionError: Expected executing the XPath "(1)[fn:has-children()]" to resolve to one of the expected results, but got AssertionError: expected [Function] to throw error matching /XPTY0004/ but got 'Cannot read properties of undefined (…'.
-fn-has-children-008,AssertionError: Expected executing the XPath "(fn:concat#2)[fn:has-children()]" to resolve to one of the expected results, but got AssertionError: expected [Function] to throw error matching /XPTY0004/ but got 'Cannot read properties of undefined (…'.
fn-has-children-012,AssertionError: expected [Function] to throw an error
fn-has-children-013,AssertionError: expected [Function] to throw an error
-fn-has-children-014,AssertionError: expected [Function] to throw error matching /XPTY0004/ but got 'Cannot read properties of undefined (…'
fn-hours-from-dateTime-3,Error: XPST0017: Function Q{http://www.w3.org/2005/xpath-functions}adjust-dateTime-to-timezone with arity of 2 not registered. Did you mean "Q{test}custom-dateTime-function ()"?
fn-hours-from-time-4,Error: XPST0017: Function Q{http://www.w3.org/2005/xpath-functions}adjust-time-to-timezone with arity of 2 not registered. No similar functions found.
fn-id-4,Error: No selector counterpart for: computedDocumentConstructor.
@@ -981,8 +978,6 @@ fn-node-name-15,Error: No selector counterpart for: computedDocumentConstructor.
fn-node-name-16,Error: No selector counterpart for: computedDocumentConstructor.
fn-node-name-17,Error: No selector counterpart for: computedDocumentConstructor.
fn-node-name-28,Error: No selector counterpart for: computedNamespaceConstructor.
-fn-node-name-30,AssertionError: expected [Function] to throw error matching /XPTY0004/ but got 'Cannot read properties of undefined (…'
-fn-node-name-31,AssertionError: expected [Function] to throw error matching /XPTY0004/ but got 'Cannot read properties of undefined (…'
fn-notintg1args-1,Error: FOCA0003: can not cast -999999999999999999 to xs:integer, it is out of bounds for JavaScript numbers.
fn-notintg1args-2,Error: FOCA0003: can not cast 830993497117024304 to xs:integer, it is out of bounds for JavaScript numbers.
fn-notintg1args-3,Error: FOCA0003: can not cast 999999999999999999 to xs:integer, it is out of bounds for JavaScript numbers.
@@ -2461,8 +2456,8 @@ cbcl-cast-gYearMonth-003,AssertionError: expected [Function] to throw an error
XQueryComment014,Error: 3: 4: 5: "10" cast as (: type comment :) xs:integer ? ^ 6: 7: = 10 Error: XPST0003: Failed to parse script. Expected end of input at <>:5:44 - 5:45
Constr-compattr-compname-20,Error: XQDY0074: The value "Q{http://example.com/x}y" of a name expressions cannot be converted to an expanded QName.
Constr-compattr-compname-21,Error: XQDY0074: The value " Q{}y " of a name expressions cannot be converted to an expanded QName.
-Constr-compattr-compname-22,Error: XQDY0074: The value " Q{http://example.com/x}y2024" of a name expressions cannot be converted to an expanded QName.
-Constr-compattr-compname-23,Error: XQDY0074: The value "Q{}y2024" of a name expressions cannot be converted to an expanded QName.
+Constr-compattr-compname-22,Error: XQDY0074: The value " Q{http://example.com/x}y2025" of a name expressions cannot be converted to an expanded QName.
+Constr-compattr-compname-23,Error: XQDY0074: The value "Q{}y2025" of a name expressions cannot be converted to an expanded QName.
Constr-compattr-id-2,AssertionError: Expected executing the XPath "element elem {attribute xml:id {" ab c d "}}" to resolve to one of the expected results, but got AssertionError: Expected XPath element elem {attribute xml:id {" ab c d "}} to resolve to the given XML. Expected
+ Freude, schöner Götterfunken,
+ Tochter aus Elysium,
+ Wir betreten feuertrunken,
+ Himmlische, dein Heiligtum.
element of the document', () => { documentNode = slimdom.parseXmlDocument( `