Skip to content

Commit 06a6b75

Browse files
andrew-colemanmattbaileyuk
authored andcommitted
fix error inserts generated in functions
1 parent afe903c commit 06a6b75

File tree

6 files changed

+93
-39
lines changed

6 files changed

+93
-39
lines changed

src/jsonata.js

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,13 +1234,22 @@ var jsonata = (function() {
12341234
}
12351235
}
12361236
// apply the procedure
1237+
var procName = expr.procedure.type === 'path' ? expr.procedure.steps[0].value : expr.procedure.value;
12371238
try {
1239+
if(typeof proc === 'object') {
1240+
proc.token = procName;
1241+
proc.position = expr.position;
1242+
}
12381243
result = yield * apply(proc, evaluatedArgs, input, environment);
12391244
} catch (err) {
1240-
// add the position field to the error
1241-
err.position = expr.position;
1242-
// and the function identifier
1243-
err.token = expr.procedure.type === 'path' ? expr.procedure.steps[0].value : expr.procedure.value;
1245+
if(!err.position) {
1246+
// add the position field to the error
1247+
err.position = expr.position;
1248+
}
1249+
if (!err.token) {
1250+
// and the function identifier
1251+
err.token = procName;
1252+
}
12441253
throw err;
12451254
}
12461255
return result;
@@ -1262,6 +1271,10 @@ var jsonata = (function() {
12621271
// the function returned a tail-call thunk
12631272
// unpack it, evaluate its arguments, and apply the tail call
12641273
var next = yield * evaluate(result.body.procedure, result.input, result.environment);
1274+
if(result.body.procedure.type === 'variable') {
1275+
next.token = result.body.procedure.value;
1276+
}
1277+
next.position = result.body.procedure.position;
12651278
var evaluatedArgs = [];
12661279
for(var ii = 0; ii < result.body.arguments.length; ii++) {
12671280
evaluatedArgs.push(yield * evaluate(result.body.arguments[ii], result.input, result.environment));
@@ -1282,40 +1295,50 @@ var jsonata = (function() {
12821295
*/
12831296
function* applyInner(proc, args, input, environment) {
12841297
var result;
1285-
var validatedArgs = args;
1286-
if(proc) {
1287-
validatedArgs = validateArguments(proc.signature, args, input);
1288-
}
1298+
try {
1299+
var validatedArgs = args;
1300+
if (proc) {
1301+
validatedArgs = validateArguments(proc.signature, args, input);
1302+
}
12891303

1290-
if (isLambda(proc)) {
1291-
result = yield * applyProcedure(proc, validatedArgs);
1292-
} else if (proc && proc._jsonata_function === true) {
1293-
var focus = {
1294-
environment: environment,
1295-
input: input
1296-
};
1297-
// the `focus` is passed in as the `this` for the invoked function
1298-
result = proc.implementation.apply(focus, validatedArgs);
1299-
// `proc.implementation` might be a generator function
1300-
// and `result` might be a generator - if so, yield
1301-
if(isIterable(result)) {
1302-
result = yield *result;
1304+
if (isLambda(proc)) {
1305+
result = yield* applyProcedure(proc, validatedArgs);
1306+
} else if (proc && proc._jsonata_function === true) {
1307+
var focus = {
1308+
environment: environment,
1309+
input: input
1310+
};
1311+
// the `focus` is passed in as the `this` for the invoked function
1312+
result = proc.implementation.apply(focus, validatedArgs);
1313+
// `proc.implementation` might be a generator function
1314+
// and `result` might be a generator - if so, yield
1315+
if (isIterable(result)) {
1316+
result = yield* result;
1317+
}
1318+
} else if (typeof proc === 'function') {
1319+
// typically these are functions that are returned by the invocation of plugin functions
1320+
// the `input` is being passed in as the `this` for the invoked function
1321+
// this is so that functions that return objects containing functions can chain
1322+
// e.g. $func().next().next()
1323+
result = proc.apply(input, validatedArgs);
1324+
/* istanbul ignore next */
1325+
if (isIterable(result)) {
1326+
result = yield* result;
1327+
}
1328+
} else {
1329+
throw {
1330+
code: "T1006",
1331+
stack: (new Error()).stack
1332+
};
13031333
}
1304-
} else if (typeof proc === 'function') {
1305-
// typically these are functions that are returned by the invocation of plugin functions
1306-
// the `input` is being passed in as the `this` for the invoked function
1307-
// this is so that functions that return objects containing functions can chain
1308-
// e.g. $func().next().next()
1309-
result = proc.apply(input, validatedArgs);
1310-
/* istanbul ignore next */
1311-
if(isIterable(result)) {
1312-
result = yield *result;
1334+
} catch(err) {
1335+
if(proc) {
1336+
if (typeof err.token == 'undefined' && typeof proc.token !== 'undefined') {
1337+
err.token = proc.token;
1338+
}
1339+
err.position = proc.position;
13131340
}
1314-
} else {
1315-
throw {
1316-
code: "T1006",
1317-
stack: (new Error()).stack
1318-
};
1341+
throw err;
13191342
}
13201343
return result;
13211344
}

test/implementation-tests.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ describe("Tests that use the $clone() function", () => {
296296
expr.evaluate(testdata2);
297297
})
298298
.to.throw()
299-
.to.deep.contain({ position: 21, code: 'T2013' });
299+
.to.deep.contain({ code: 'T2013' });
300300
});
301301
});
302302
});
@@ -829,20 +829,20 @@ describe("Tests that include infinite recursion", () => {
829829
expr.evaluate();
830830
})
831831
.to.throw()
832-
.to.deep.contain({ position: 46, code: "U1001" });
832+
.to.deep.contain({ token: "inf", position: 32, code: "U1001" });
833833
});
834834
});
835835

836836
describe("stack overflow - infinite recursive function - tail call", function() {
837837
this.timeout(5000);
838838
it("should throw error", function() {
839839
expect(function() {
840-
var expr = jsonata("(" + " $inf := function(){$inf()};" + " $inf()" + ")");
840+
var expr = jsonata("( $inf := function(){$inf()}; $inf())");
841841
timeboxExpression(expr, 1000, 500);
842842
expr.evaluate();
843843
})
844844
.to.throw()
845-
.to.deep.contain({ position: 37, code: "U1001" });
845+
.to.deep.contain({ token: "inf", code: "U1001" });
846846
});
847847
});
848848
});

test/run-test-suite-async.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ describe("JSONata Test Suite - async mode", () => {
134134
// Second is that a (defined) result was provided. In this case,
135135
// we do a deep equality check against the expected result.
136136
return expect(jsonataPromise(expr, dataset, testcase.bindings)).to.eventually.deep.equal(testcase.result);
137+
} else if ("error" in testcase) {
138+
// If an error was expected,
139+
// we do a deep equality check against the expected error structure.
140+
return expect(jsonataPromise(expr, dataset, testcase.bindings)).to.be.rejected
141+
.and.eventually.have.property('code', testcase.error.code);
137142
} else if ("code" in testcase) {
138143
// Finally, if a `code` field was specified, we expected the
139144
// evaluation to fail and include the specified code in the

test/run-test-suite.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ describe("JSONata Test Suite", () => {
118118
// we do a deep equality check against the expected result.
119119
let result = expr.evaluate(dataset, testcase.bindings);
120120
expect(result).to.deep.equal(testcase.result);
121+
} else if ("error" in testcase) {
122+
// If an error was expected,
123+
// we do a deep equality check against the expected error structure.
124+
expect(function() {
125+
expr.evaluate(dataset, testcase.bindings);
126+
})
127+
.to.throw()
128+
.to.deep.contain(testcase.error);
121129
} else if ("code" in testcase) {
122130
// Finally, if a `code` field was specified, we expected the
123131
// evaluation to fail and include the specified code in the
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"expr": "( $A := function(){$min(2, 3)}; $A() )",
3+
"dataset": null,
4+
"bindings": {},
5+
"error": {
6+
"code": "T0410",
7+
"token": "min"
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"expr": "( $B := function(){''}; $A := function(){2 + $B()}; $A() )",
3+
"dataset": null,
4+
"bindings": {},
5+
"error": {
6+
"code": "T2002",
7+
"token": "+"
8+
}
9+
}

0 commit comments

Comments
 (0)