Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:
fail-fast: false
matrix:
node-version:
- 22
- 20
- 18
- 16
steps:
- uses: actions/checkout@v2
with:
Expand Down
33 changes: 33 additions & 0 deletions src/expressions/functions/argumentHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ 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,
sequenceTypeToString,
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,
Expand Down Expand Up @@ -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);
};
}
42 changes: 10 additions & 32 deletions src/expressions/functions/builtInFunctions_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 },
Expand All @@ -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 },
Expand Down Expand Up @@ -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 },
Expand All @@ -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 },
Expand All @@ -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 },
Expand All @@ -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 },
Expand All @@ -614,15 +592,15 @@ 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 },
},

{
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 },
Expand Down Expand Up @@ -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 },
Expand Down
34 changes: 10 additions & 24 deletions src/expressions/functions/builtInFunctions_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,14 @@ 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;

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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
},

{
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 4 additions & 9 deletions test/assets/unrunnableTestCases.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 <elem xml:id=" ab c d "/> to equal <elem xml:id="ab c d"/>, AssertionError: expected [Function] to throw an error.
K2-ComputeConAttr-34,AssertionError: Expected XPath <e> { attribute name {xs:hexBinary("ff")}, attribute name2 {"content"} } </e> to resolve to the given XML. Expected <e name="ff" name2="content"/> to equal <e name="FF" name2="content"/>
K2-ComputeConAttr-48,AssertionError: Expected executing the XPath "string(attribute xml:id {" ab c d "})" to resolve to one of the expected results, but got AssertionError: string(attribute xml:id {" ab c d "}): expected ' ab c d ' to equal 'ab c d', AssertionError: expected [Function] to throw an error.
Expand Down Expand Up @@ -2548,8 +2543,8 @@ cbcl-constr-compcomment-001,AssertionError: expected [Function] to throw an erro
cbcl-constr-compcomment-002,AssertionError: expected [Function] to throw an error
Constr-compelem-name-20,Error: XQDY0074: The value "Q{http://example.com/x}x" of a name expressions cannot be converted to an expanded QName.
Constr-compelem-name-21,Error: XQDY0074: The value "Q{}x" of a name expressions cannot be converted to an expanded QName.
Constr-compelem-name-22,Error: XQDY0074: The value " Q{http://example.com/x}x2024" of a name expressions cannot be converted to an expanded QName.
Constr-compelem-name-23,Error: XQDY0074: The value " Q{}x2024 " of a name expressions cannot be converted to an expanded QName.
Constr-compelem-name-22,Error: XQDY0074: The value " Q{http://example.com/x}x2025" of a name expressions cannot be converted to an expanded QName.
Constr-compelem-name-23,Error: XQDY0074: The value " Q{}x2025 " of a name expressions cannot be converted to an expanded QName.
Constr-compelem-name-24,AssertionError: element {" x" || year-from-date(current-date()) || " "} {}: expected false to be true
Constr-compelem-constrmod-3,AssertionError: expected [Function] to throw error matching /FORG0001/ but got 'Not implemented: only module imports,…'
Constr-compelem-constrmod-4,AssertionError: Expected executing the XPath "declare construction preserve; (element elem {xs:decimal((//decimal[1]))}) cast as xs:integer" to resolve to one of the expected results, but got Error: Not implemented: only module imports, namespace declarations, and function declarations are implemented in XQuery modules, AssertionError: expected [Function] to throw error matching /FORG0001/ but got 'Not implemented: only module imports,…'.
Expand Down
16 changes: 16 additions & 0 deletions test/specs/parsing/functions/functions.node.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,22 @@ return $node/root() = $element`,
);
});

it('returns the correct error if the context argument is of the wrong type', () => {
documentNode = slimdom.parseXmlDocument(
`<p xmlns="http://example.com/one" xml:lang="de" author="Friedrich von Schiller">
Freude, schöner Götterfunken,<br/>
Tochter aus Elysium,<br/>
Wir betreten feuertrunken,<br/>
Himmlische, dein Heiligtum.</p>`,
);

chai.assert.throws(() => {
evaluateXPathToString(`'string'!path()`, documentNode, null, null, {
language: evaluateXPath.XQUERY_3_1_LANGUAGE,
});
}, 'XPTY0004');
});

it('returns "/Q{http://example.com/one}p[1]" for the <p> element of the document', () => {
documentNode = slimdom.parseXmlDocument(
`<p xmlns="http://example.com/one" xml:lang="de" author="Friedrich von Schiller">
Expand Down
Loading