Repository: FontoXML/fontoxpath
Pull request #519 (2022-08-03 12:58:27)

Better trace

bundlemon[bot]: <!-- bundlemon -->
## BundleMon

  
  
  
  
  
<details open>
<summary>Files updated (2)</summary>

Status | Path | Size | Limits
:------------: | ------------ | :------------: | :------------:
:white_check_mark: | fontoxpath.esm.js<br/> | 81.86KB (+111B +0.13%) | -
:white_check_mark: | fontoxpath.js<br/> | 81.75KB (+111B +0.13%) | -
</details>

  
  


Total files change +222B +0.13%

Final result: :white_check_mark:

[View report in BundleMon website ➡️](https://app.bundlemon.dev/projects/609024effb87a20009a6b096/reports/62ea714a29bfb7ac07d7f1e3)

---
<p align="center"><a href="https://app.bundlemon.dev/projects/609024effb87a20009a6b096/reports?branch=better-trace&resolution=all" target="_blank" rel="noreferrer noopener">Current branch size history</a> | <a href="https://app.bundlemon.dev/projects/609024effb87a20009a6b096/reports?branch=master&resolution=days" target="_blank" rel="noreferrer noopener">Target branch size history</a></p>
coveralls: 
[![Coverage Status](https://coveralls.io/builds/51385956/badge)](https://coveralls.io/builds/51385956)

Coverage increased (+0.03%) to 91.152% when pulling **e2bfc8c122396683e2133810f56d64a16603c499 on better-trace** into **4b81076fe51a09396be150432040fa8fdcab2cd8 on master**.


Patch:
diff --git a/src/expressions/dataTypes/Sequences/ArrayBackedSequence.ts b/src/expressions/dataTypes/Sequences/ArrayBackedSequence.ts
index 4bfe7fc1..deb86e19 100644
--- a/src/expressions/dataTypes/Sequences/ArrayBackedSequence.ts
+++ b/src/expressions/dataTypes/Sequences/ArrayBackedSequence.ts
@@ -59,7 +59,9 @@ export default class ArrayBackedSequence implements ISequence {
 			return true;
 		}
 		// We always have a length > 1, or we'd be a singletonSequence
-		throw errFORG0006();
+		throw errFORG0006(
+			`Cannot determine the effective boolean value of a sequence with a length higher than one.`
+		);
 	}
 
 	public getLength(): number {
diff --git a/src/expressions/dataTypes/Sequences/IteratorBackedSequence.ts b/src/expressions/dataTypes/Sequences/IteratorBackedSequence.ts
index 35aa6cf4..cbdee788 100644
--- a/src/expressions/dataTypes/Sequences/IteratorBackedSequence.ts
+++ b/src/expressions/dataTypes/Sequences/IteratorBackedSequence.ts
@@ -127,7 +127,9 @@ export default class IteratorBackedSequence implements ISequence {
 
 		const secondValue = iterator.next(IterationHint.NONE);
 		if (!secondValue.done) {
-			throw errFORG0006();
+			throw errFORG0006(
+				`Cannot determine the effective boolean value of a sequence with a length higher than one.`
+			);
 		}
 
 		this.reset(oldPosition);
diff --git a/src/expressions/dataTypes/Sequences/getEffectiveBooleanValue.ts b/src/expressions/dataTypes/Sequences/getEffectiveBooleanValue.ts
index ff7210c8..9e707b68 100644
--- a/src/expressions/dataTypes/Sequences/getEffectiveBooleanValue.ts
+++ b/src/expressions/dataTypes/Sequences/getEffectiveBooleanValue.ts
@@ -1,6 +1,6 @@
 import { errFORG0006 } from '../../functions/FunctionOperationErrors';
 import isSubtypeOf from '../isSubtypeOf';
-import Value, { ValueType } from '../Value';
+import Value, { ValueType, valueTypeToString } from '../Value';
 
 export default function getEffectiveBooleanValue(value: Value): boolean {
 	const jsValue = value.value;
@@ -30,5 +30,9 @@ export default function getEffectiveBooleanValue(value: Value): boolean {
 	}
 
 	// In all other cases, fn:boolean raises a type error [err:FORG0006]FO31.
-	throw errFORG0006();
+	throw errFORG0006(
+		`Cannot determine the effective boolean value of a value with the type ${valueTypeToString(
+			value.type
+		)}`
+	);
 }
diff --git a/src/expressions/functions/FunctionOperationErrors.ts b/src/expressions/functions/FunctionOperationErrors.ts
index c21cf378..23d01db6 100644
--- a/src/expressions/functions/FunctionOperationErrors.ts
+++ b/src/expressions/functions/FunctionOperationErrors.ts
@@ -1,4 +1,5 @@
 export const errFOAY0001 = () => new Error('FOAY0001: Array index out of bounds');
 
-export const errFORG0006 = () =>
-	new Error('FORG0006: A wrong argument type was specified in a function call.');
+export const errFORG0006 = (
+	message: string = 'A wrong argument type was specified in a function call.'
+) => new Error(`FORG0006: ${message}`);
diff --git a/src/parseScript.ts b/src/parseScript.ts
index ae026e9d..ff1718e8 100644
--- a/src/parseScript.ts
+++ b/src/parseScript.ts
@@ -113,7 +113,7 @@ function parseNode(
 	if (typeof firstChild === 'object' && !Array.isArray(firstChild)) {
 		if (firstChild !== null) {
 			for (const attributeName of Object.keys(firstChild)) {
-				const attributeValue = firstChild[attributeName];
+				let attributeValue = firstChild[attributeName];
 				if (attributeValue === null) {
 					continue;
 				}
@@ -129,7 +129,12 @@ function parseNode(
 					}
 					continue;
 				}
-
+				if (
+					(attributeName === 'start' || attributeName === 'end') &&
+					name === 'stackTrace'
+				) {
+					attributeValue = JSON.stringify(attributeValue);
+				}
 				if (skipEmptyPrefixes && attributeName === 'prefix' && attributeValue === '') {
 					continue;
 				}
diff --git a/src/parsing/convertXmlToAst.ts b/src/parsing/convertXmlToAst.ts
index 721567d0..6b998d74 100644
--- a/src/parsing/convertXmlToAst.ts
+++ b/src/parsing/convertXmlToAst.ts
@@ -23,8 +23,16 @@ export default function convertXmlToAst(element: Element): IAST {
 	if (attributes && attributes.length > 0) {
 		ast.push(
 			Array.from(attributes).reduce<ASTAttributes>((attrs, attr) => {
-				attrs[attr.localName] =
-					attr.localName === 'type' ? stringToSequenceType(attr.value) : attr.value;
+				if (
+					attr.localName === 'start' ||
+					(attr.localName === 'end' && element.localName === 'stackTrace')
+				) {
+					attrs[attr.localName] = JSON.parse(attr.value);
+				} else if (attr.localName === 'type') {
+					attrs[attr.localName] = stringToSequenceType(attr.value);
+				} else {
+					attrs[attr.localName] = attr.value;
+				}
 				return attrs;
 			}, {})
 		);


Test patch:
diff --git a/test/specs/annotation.tests.ts b/test/specs/annotation.tests.ts
index ea77302b..a7582342 100644
--- a/test/specs/annotation.tests.ts
+++ b/test/specs/annotation.tests.ts
@@ -2,9 +2,9 @@ import * as chai from 'chai';
 import {
 	SequenceMultiplicity,
 	SequenceType,
-    sequenceTypeToString,
-    ValueType,
-    valueTypeToString,
+	sequenceTypeToString,
+	ValueType,
+	valueTypeToString,
 } from 'fontoxpath/expressions/dataTypes/Value';
 import astHelper from 'fontoxpath/parsing/astHelper';
 import parseExpression from 'fontoxpath/parsing/parseExpression';
@@ -35,7 +35,13 @@ function assertValueType(
 	);
 	const resultType = astHelper.getAttribute(upperNode, 'type') as SequenceType;
 
-	chai.assert.equal(resultType && resultType.type, expectedType, `Expected ${resultType ? sequenceTypeToString(resultType) : '"untyped type"'} to equal ${valueTypeToString(expectedType)}`);
+	chai.assert.equal(
+		resultType && resultType.type,
+		expectedType,
+		`Expected ${
+			resultType ? sequenceTypeToString(resultType) : '"untyped type"'
+		} to equal ${valueTypeToString(expectedType)}`
+	);
 }
 
 describe('Annotating constants', () => {
diff --git a/test/specs/parsing/debug/stackTrace.tests.ts b/test/specs/parsing/debug/stackTrace.tests.ts
index 588c7a99..d94419c8 100644
--- a/test/specs/parsing/debug/stackTrace.tests.ts
+++ b/test/specs/parsing/debug/stackTrace.tests.ts
@@ -2,11 +2,15 @@ import * as chai from 'chai';
 import {
 	evaluateUpdatingExpression,
 	evaluateXPath,
+	evaluateXPathToBoolean,
 	evaluateXPathToString,
+	Language,
+	parseScript,
 	registerCustomXPathFunction,
 	registerXQueryModule,
 } from 'fontoxpath';
 import * as sinon from 'sinon';
+import { Document } from 'slimdom';
 
 function a() {
 	throw new Error('Test error');
@@ -92,7 +96,7 @@ else
 3: else
 4:   (1, 2, 3)
 
-Error: FORG0006: A wrong argument type was specified in a function call.
+Error: FORG0006: Cannot determine the effective boolean value of a value with the type array(*)
   at <andOp>:2:3 - 2:11
   at <ifThenElseExpr>:1:1 - 4:12`
 		);
@@ -255,13 +259,13 @@ Test error
 			// filename, and line number. We only check these as the full error message is machine
 			// specific (may contain user name in the full file paths)
 			chai.assert.include(errorMessageLines[5], '    at a (');
-			chai.assert.include(errorMessageLines[5], 'stackTrace.tests.ts:12:8)');
+			chai.assert.include(errorMessageLines[5], 'stackTrace.tests.ts:16:8)');
 
 			chai.assert.include(errorMessageLines[6], '    at b (');
-			chai.assert.include(errorMessageLines[6], 'stackTrace.tests.ts:15:2)');
+			chai.assert.include(errorMessageLines[6], 'stackTrace.tests.ts:19:2)');
 
 			chai.assert.include(errorMessageLines[7], '    at c (');
-			chai.assert.include(errorMessageLines[7], 'stackTrace.tests.ts:18:2)');
+			chai.assert.include(errorMessageLines[7], 'stackTrace.tests.ts:22:2)');
 
 			chai.assert.equal(errorMessageLines[15], '  at <functionCallExpr>:1:1 - 1:18');
 		}
@@ -290,23 +294,23 @@ Test error
 			// filename, and line number. We only check these as the full error message is machine
 			// specific (may contain user name in the full file paths)
 			chai.assert.include(error.message, '    at a (');
-			chai.assert.include(error.message, 'stackTrace.tests.ts:12:8)');
+			chai.assert.include(error.message, 'stackTrace.tests.ts:16:8)');
 
 			chai.assert.include(error.message, '    at b (');
-			chai.assert.include(error.message, 'stackTrace.tests.ts:15:2)');
+			chai.assert.include(error.message, 'stackTrace.tests.ts:19:2)');
 
 			chai.assert.include(error.message, '    at c (');
-			chai.assert.include(error.message, 'stackTrace.tests.ts:18:2)');
+			chai.assert.include(error.message, 'stackTrace.tests.ts:22:2)');
 
 			chai.assert.include(error.message, '  at <functionCallExpr>:1:15 - 1:32');
 			chai.assert.include(error.message, '  at <andOp>:1:4 - 1:32');
 			chai.assert.include(error.message, '  at <ifThenElseExpr>:1:1 - 1:57');
 
 			chai.assert.include(error.message, '    at e (');
-			chai.assert.include(error.message, 'stackTrace.tests.ts:34:2)');
+			chai.assert.include(error.message, 'stackTrace.tests.ts:38:2)');
 
 			chai.assert.include(error.message, '    at f (');
-			chai.assert.include(error.message, 'stackTrace.tests.ts:38:2)');
+			chai.assert.include(error.message, 'stackTrace.tests.ts:42:2)');
 
 			chai.assert.include(error.message, '  at <functionCallExpr>:1:1 - 1:18');
 		}
@@ -326,4 +330,28 @@ Error: XPST0008, The variable map is not in scope.
   at <pathExpr>:1:1 - 1:12`
 		);
 	});
+
+	it('Shows a nice stacktrace for a separate module', () => {
+		const queryAst = parseScript(
+			'fn:boolean((1,2,3))',
+			{
+				language: Language.XQUERY_3_1_LANGUAGE,
+				debug: true,
+			},
+			new Document()
+		);
+
+		chai.assert.throws(
+			() =>
+				evaluateXPathToBoolean(queryAst, null, null, null, {
+					debug: true,
+					disableCache: true,
+				}),
+			`1: fn:boolean((1,2,3))
+   ^^^^^^^^^^^^^^^^^^^
+
+Error: FORG0006: Cannot determine the effective boolean value of a sequence with a length higher than one.
+  at <functionCallExpr>:1:1 - 1:20`
+		);
+	});
 });
diff --git a/test/specs/parsing/postfix/Lookup.tests.ts b/test/specs/parsing/postfix/Lookup.tests.ts
index 25908fb3..28e3fe16 100644
--- a/test/specs/parsing/postfix/Lookup.tests.ts
+++ b/test/specs/parsing/postfix/Lookup.tests.ts
@@ -68,11 +68,7 @@ describe('lookup operator', () => {
 
 		it('Annotatest correctly', () => {
 			// This caused a bug because the lookup was type-found to a map.
-			chai.assert.isTrue(
-				evaluateXPathToBoolean(
-					'map { "first" : 2}?first * 10 = 20'
-				)
-			);
+			chai.assert.isTrue(evaluateXPathToBoolean('map { "first" : 2}?first * 10 = 20'));
 		});
 
 		it('can lookup in an array', () => {