Skip to content

Commit 1687892

Browse files
committed
- Enhancement: Use more efficient new Function over eval;
also allows use of cyclic context objects
1 parent e35e178 commit 1687892

File tree

9 files changed

+187
-48
lines changed

9 files changed

+187
-48
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## 1.2.0 (October 13, 2019)
44

55
- Enhancement: Add `@root` filter selector
6+
- Enhancement: Use more efficient `new Function` over `eval`;
7+
also allows use of cyclic context objects
68
- npm: Update devDeps and `package-lock.json`
79

810
## 1.1.0 (September 26, 2019)

dist/index-es.js

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,29 @@ function _possibleConstructorReturn(self, call) {
133133
return _assertThisInitialized(self);
134134
}
135135

136-
/* eslint-disable no-eval, prefer-named-capture-group */
137-
// Disabled `prefer-named-capture-group` due to https://github.com/babel/babel/issues/8951#issuecomment-508045524
138-
var globalEval = eval; // Only Node.JS has a process variable that is of [[Class]] process
136+
function _toConsumableArray(arr) {
137+
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
138+
}
139+
140+
function _arrayWithoutHoles(arr) {
141+
if (Array.isArray(arr)) {
142+
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
143+
144+
return arr2;
145+
}
146+
}
147+
148+
function _iterableToArray(iter) {
149+
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
150+
}
151+
152+
function _nonIterableSpread() {
153+
throw new TypeError("Invalid attempt to spread non-iterable instance");
154+
}
139155

156+
/* eslint-disable prefer-named-capture-group */
157+
// Disabled `prefer-named-capture-group` due to https://github.com/babel/babel/issues/8951#issuecomment-508045524
158+
// Only Node.JS has a process variable that is of [[Class]] process
140159
var supportsNodeVM = function supportsNodeVM() {
141160
try {
142161
return Object.prototype.toString.call(global.process) === '[object process]';
@@ -190,22 +209,31 @@ var vm = supportsNodeVM() ? require('vm') : {
190209
var funcs = [];
191210
moveToAnotherArray(keys, funcs, function (key) {
192211
return typeof context[key] === 'function';
212+
}); // Todo[engine:node@>=8]: Use the next line instead of the
213+
// succeeding
214+
// const values = Object.values(context);
215+
216+
var values = keys.map(function (vr, i) {
217+
return context[vr];
193218
});
194-
var code = funcs.reduce(function (s, func) {
219+
var funcString = funcs.reduce(function (s, func) {
195220
var fString = context[func].toString();
196221

197222
if (!/function/.exec(fString)) {
198223
fString = 'function ' + fString;
199224
}
200225

201226
return 'var ' + func + '=' + fString + ';' + s;
202-
}, '') + keys.reduce(function (s, vr) {
203-
return 'var ' + vr + '=' + JSON.stringify(context[vr]).replace( // http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
204-
/\u2028|\u2029/g, function (m) {
205-
return "\\u202" + (m === "\u2028" ? '8' : '9');
206-
}) + ';' + s;
207-
}, expr);
208-
return globalEval(code);
227+
}, ''); // Remove last semi so `return` will be inserted before
228+
// the previous one instead, allowing for the return
229+
// of a bare ending expression
230+
231+
expr = (funcString + expr).replace(/;[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*$/, ''); // Insert `return`
232+
233+
var lastStatementEnd = expr.lastIndexOf(';');
234+
var code = lastStatementEnd > -1 ? expr.slice(0, lastStatementEnd + 1) + ' return ' + expr.slice(lastStatementEnd + 1) : ' return ' + expr; // eslint-disable-next-line no-new-func
235+
236+
return _construct(Function, _toConsumableArray(keys).concat([code])).apply(void 0, _toConsumableArray(values));
209237
}
210238
};
211239
/**
@@ -274,6 +302,7 @@ function (_Error) {
274302
* @param {string|PlainObject} preferredOutput
275303
* @param {"value"|"property"} type
276304
* @param {ReturnObject} fullRetObj
305+
* @returns {void}
277306
*/
278307

279308
/**
@@ -282,16 +311,35 @@ function (_Error) {
282311
* @param {string} path
283312
* @param {PlainObject|GenericArray} parent
284313
* @param {string} parentPropName
314+
* @returns {boolean}
285315
*/
286316

287317
/**
288-
* @param {PlainObject} [opts] If present, must be an object
289-
* @param {string} expr JSON path to evaluate
290-
* @param {JSON} obj JSON object to evaluate against
291-
* @param {JSONPathCallback} callback Passed 3 arguments: 1) desired payload
318+
* @typedef {PlainObject} JSONPathOptions
319+
* @property {JSON} json
320+
* @property {string|string[]} path
321+
* @property {"value"|"path"|"pointer"|"parent"|"parentProperty"|"all"}
322+
* [resultType="value"]
323+
* @property {boolean} [flatten=false]
324+
* @property {boolean} [wrap=true]
325+
* @property {PlainObject} [sandbox={}]
326+
* @property {boolean} [preventEval=false]
327+
* @property {PlainObject|GenericArray|null} [parent=null]
328+
* @property {string|null} [parentProperty=null]
329+
* @property {JSONPathCallback} [callback]
330+
* @property {OtherTypeCallback} [otherTypeCallback] Defaults to
331+
* function which throws on encountering `@other`
332+
* @property {boolean} [autostart=true]
333+
*/
334+
335+
/**
336+
* @param {string|JSONPathOptions} opts If a string, will be treated as `expr`
337+
* @param {string} [expr] JSON path to evaluate
338+
* @param {JSON} [obj] JSON object to evaluate against
339+
* @param {JSONPathCallback} [callback] Passed 3 arguments: 1) desired payload
292340
* per `resultType`, 2) `"value"|"property"`, 3) Full returned object with
293341
* all payloads
294-
* @param {OtherTypeCallback} otherTypeCallback If `@other()` is at the end
342+
* @param {OtherTypeCallback} [otherTypeCallback] If `@other()` is at the end
295343
* of one's query, this will be invoked with the value of the item, its
296344
* path, its parent, and its parent's property name, and it should return
297345
* a boolean indicating whether the supplied value belongs to the "other"

dist/index-es.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index-es.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index-umd.js

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,29 @@
139139
return _assertThisInitialized(self);
140140
}
141141

142-
/* eslint-disable no-eval, prefer-named-capture-group */
143-
// Disabled `prefer-named-capture-group` due to https://github.com/babel/babel/issues/8951#issuecomment-508045524
144-
var globalEval = eval; // Only Node.JS has a process variable that is of [[Class]] process
142+
function _toConsumableArray(arr) {
143+
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
144+
}
145+
146+
function _arrayWithoutHoles(arr) {
147+
if (Array.isArray(arr)) {
148+
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
149+
150+
return arr2;
151+
}
152+
}
153+
154+
function _iterableToArray(iter) {
155+
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
156+
}
157+
158+
function _nonIterableSpread() {
159+
throw new TypeError("Invalid attempt to spread non-iterable instance");
160+
}
145161

162+
/* eslint-disable prefer-named-capture-group */
163+
// Disabled `prefer-named-capture-group` due to https://github.com/babel/babel/issues/8951#issuecomment-508045524
164+
// Only Node.JS has a process variable that is of [[Class]] process
146165
var supportsNodeVM = function supportsNodeVM() {
147166
try {
148167
return Object.prototype.toString.call(global.process) === '[object process]';
@@ -196,22 +215,31 @@
196215
var funcs = [];
197216
moveToAnotherArray(keys, funcs, function (key) {
198217
return typeof context[key] === 'function';
218+
}); // Todo[engine:node@>=8]: Use the next line instead of the
219+
// succeeding
220+
// const values = Object.values(context);
221+
222+
var values = keys.map(function (vr, i) {
223+
return context[vr];
199224
});
200-
var code = funcs.reduce(function (s, func) {
225+
var funcString = funcs.reduce(function (s, func) {
201226
var fString = context[func].toString();
202227

203228
if (!/function/.exec(fString)) {
204229
fString = 'function ' + fString;
205230
}
206231

207232
return 'var ' + func + '=' + fString + ';' + s;
208-
}, '') + keys.reduce(function (s, vr) {
209-
return 'var ' + vr + '=' + JSON.stringify(context[vr]).replace( // http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
210-
/\u2028|\u2029/g, function (m) {
211-
return "\\u202" + (m === "\u2028" ? '8' : '9');
212-
}) + ';' + s;
213-
}, expr);
214-
return globalEval(code);
233+
}, ''); // Remove last semi so `return` will be inserted before
234+
// the previous one instead, allowing for the return
235+
// of a bare ending expression
236+
237+
expr = (funcString + expr).replace(/;[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*$/, ''); // Insert `return`
238+
239+
var lastStatementEnd = expr.lastIndexOf(';');
240+
var code = lastStatementEnd > -1 ? expr.slice(0, lastStatementEnd + 1) + ' return ' + expr.slice(lastStatementEnd + 1) : ' return ' + expr; // eslint-disable-next-line no-new-func
241+
242+
return _construct(Function, _toConsumableArray(keys).concat([code])).apply(void 0, _toConsumableArray(values));
215243
}
216244
};
217245
/**
@@ -280,6 +308,7 @@
280308
* @param {string|PlainObject} preferredOutput
281309
* @param {"value"|"property"} type
282310
* @param {ReturnObject} fullRetObj
311+
* @returns {void}
283312
*/
284313

285314
/**
@@ -288,16 +317,35 @@
288317
* @param {string} path
289318
* @param {PlainObject|GenericArray} parent
290319
* @param {string} parentPropName
320+
* @returns {boolean}
291321
*/
292322

293323
/**
294-
* @param {PlainObject} [opts] If present, must be an object
295-
* @param {string} expr JSON path to evaluate
296-
* @param {JSON} obj JSON object to evaluate against
297-
* @param {JSONPathCallback} callback Passed 3 arguments: 1) desired payload
324+
* @typedef {PlainObject} JSONPathOptions
325+
* @property {JSON} json
326+
* @property {string|string[]} path
327+
* @property {"value"|"path"|"pointer"|"parent"|"parentProperty"|"all"}
328+
* [resultType="value"]
329+
* @property {boolean} [flatten=false]
330+
* @property {boolean} [wrap=true]
331+
* @property {PlainObject} [sandbox={}]
332+
* @property {boolean} [preventEval=false]
333+
* @property {PlainObject|GenericArray|null} [parent=null]
334+
* @property {string|null} [parentProperty=null]
335+
* @property {JSONPathCallback} [callback]
336+
* @property {OtherTypeCallback} [otherTypeCallback] Defaults to
337+
* function which throws on encountering `@other`
338+
* @property {boolean} [autostart=true]
339+
*/
340+
341+
/**
342+
* @param {string|JSONPathOptions} opts If a string, will be treated as `expr`
343+
* @param {string} [expr] JSON path to evaluate
344+
* @param {JSON} [obj] JSON object to evaluate against
345+
* @param {JSONPathCallback} [callback] Passed 3 arguments: 1) desired payload
298346
* per `resultType`, 2) `"value"|"property"`, 3) Full returned object with
299347
* all payloads
300-
* @param {OtherTypeCallback} otherTypeCallback If `@other()` is at the end
348+
* @param {OtherTypeCallback} [otherTypeCallback] If `@other()` is at the end
301349
* of one's query, this will be invoked with the value of the item, its
302350
* path, its parent, and its parent's property name, and it should return
303351
* a boolean indicating whether the supplied value belongs to the "other"

0 commit comments

Comments
 (0)